Compare commits
56 Commits
07388e30da
...
upgrade
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
732afaf623 | ||
|
|
48aeaabeea | ||
|
|
a4b9fe9065 | ||
|
|
31e50af593 | ||
|
|
39053cadaa | ||
|
|
ea6656aa33 | ||
|
|
92c4987372 | ||
|
|
b3ebf46afa | ||
|
|
c0ef535001 | ||
|
|
81329e4354 | ||
|
|
36f3bb77d4 | ||
|
|
6760ea46f9 | ||
|
|
e4164cbc71 | ||
|
|
94ea44c66e | ||
|
|
7441d1ac58 | ||
|
|
7ff26229d9 | ||
|
|
b93ce38d48 | ||
|
|
ae934e77f4 | ||
|
|
cae5fa1c71 | ||
|
|
fc3f626313 | ||
|
|
1027b49258 | ||
|
|
5e20b94a1f | ||
|
|
f99d6efaf9 | ||
|
|
602e6e3473 | ||
|
|
285a33c488 | ||
|
|
cac42c9b29 | ||
|
|
498010e3e2 | ||
|
|
c170e1e1fe | ||
|
|
cde74a04d5 | ||
|
|
9a105b30c6 | ||
|
|
8efc0a7a4b | ||
|
|
d11e50d4ef | ||
|
|
4bc25fb0bc | ||
|
|
3be2284da9 | ||
|
|
a35c688a64 | ||
|
|
617a94262b | ||
|
|
3fe92be980 | ||
|
|
7d3c0a3fa4 | ||
|
|
a926dcde07 | ||
|
|
e20582897f | ||
|
|
cf09014d50 | ||
|
|
0169468114 | ||
|
|
9d92a8a510 | ||
|
|
51e3d5141c | ||
|
|
97c8c9e72b | ||
|
|
3ffa6b03c7 | ||
|
|
8dfff139e1 | ||
|
|
22be6682dd | ||
|
|
c74da09bde | ||
|
|
c5d69f1a19 | ||
|
|
2396464a19 | ||
|
|
cb83ea06fa | ||
| d2e6849bca | |||
|
|
2f6950ee9e | ||
| 1a21171ae8 | |||
| 0439e4c8b9 |
@@ -1,6 +0,0 @@
|
|||||||
-- Note: It's highly recommended to use more secure passwords
|
|
||||||
|
|
||||||
-- create db/user for application data/drizzle orm
|
|
||||||
CREATE USER app_svc WITH PASSWORD 'password';
|
|
||||||
CREATE DATABASE app_db;
|
|
||||||
GRANT ALL PRIVILEGES ON DATABASE app_db TO app_svc;
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,3 +20,4 @@ yarn.lock
|
|||||||
/scripts/
|
/scripts/
|
||||||
/pb_data/
|
/pb_data/
|
||||||
/.tanstack/
|
/.tanstack/
|
||||||
|
/dist/
|
||||||
16
.nitro/types/nitro-config.d.ts
vendored
Normal file
16
.nitro/types/nitro-config.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// Generated by nitro
|
||||||
|
|
||||||
|
// App Config
|
||||||
|
import type { Defu } from 'defu'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type UserAppConfig = Defu<{}, []>
|
||||||
|
|
||||||
|
declare module "nitropack/types" {
|
||||||
|
interface AppConfig extends UserAppConfig {}
|
||||||
|
interface NitroRuntimeConfig {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export {}
|
||||||
1
.nitro/types/nitro-imports.d.ts
vendored
Normal file
1
.nitro/types/nitro-imports.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export {}
|
||||||
8
.nitro/types/nitro-routes.d.ts
vendored
Normal file
8
.nitro/types/nitro-routes.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Generated by nitro
|
||||||
|
import type { Serialize, Simplify } from "nitropack/types";
|
||||||
|
declare module "nitropack/types" {
|
||||||
|
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T
|
||||||
|
interface InternalApi {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export {}
|
||||||
3
.nitro/types/nitro.d.ts
vendored
Normal file
3
.nitro/types/nitro.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/// <reference path="./nitro-routes.d.ts" />
|
||||||
|
/// <reference path="./nitro-config.d.ts" />
|
||||||
|
/// <reference path="./nitro-imports.d.ts" />
|
||||||
317
brackets/10.json
Normal file
317
brackets/10.json
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 9,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_seed": 10,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 1,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 1,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_seed": 5,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 1,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 1,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_seed": 6,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 2,
|
||||||
|
"order": 11,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 2,
|
||||||
|
"order": 12,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 3,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 19,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 4,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 24,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 5,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 25,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 25,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 11,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": 8,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 1,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 1,
|
||||||
|
"order": 10,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 2,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 16,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 2,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 17,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 3,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 4,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 22,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
323
brackets/11.json
Normal file
323
brackets/11.json
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 9,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_seed": 10,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6,
|
||||||
|
"away_seed": 11,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 1,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 1,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_seed": 5,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 1,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 1,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 2,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 2,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 3,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 19,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 4,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 24,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 5,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 25,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 25,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": 10,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 11,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": 8,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 1,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 1,
|
||||||
|
"order": 10,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 14,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 2,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 16,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 2,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 17,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 3,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 4,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 22,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
375
brackets/12.json
Normal file
375
brackets/12.json
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 9,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5,
|
||||||
|
"away_seed": 12,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_seed": 10,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6,
|
||||||
|
"away_seed": 11,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 1,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 1,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 1,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 1,
|
||||||
|
"order": 8,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 28,
|
||||||
|
"round": 2,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 29,
|
||||||
|
"round": 2,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 34,
|
||||||
|
"round": 3,
|
||||||
|
"order": 23,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 28,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 29,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 37,
|
||||||
|
"round": 4,
|
||||||
|
"order": 26,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 34,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 36,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 38,
|
||||||
|
"round": 5,
|
||||||
|
"order": 27,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 37,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 37,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 0,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 11,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 0,
|
||||||
|
"order": 10,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 0,
|
||||||
|
"order": 11,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 0,
|
||||||
|
"order": 12,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 1,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 11,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 1,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 1,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 27,
|
||||||
|
"round": 1,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 30,
|
||||||
|
"round": 2,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 24,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 25,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 31,
|
||||||
|
"round": 2,
|
||||||
|
"order": 20,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 26,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 27,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 32,
|
||||||
|
"round": 3,
|
||||||
|
"order": 21,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 28,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 30,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 33,
|
||||||
|
"round": 3,
|
||||||
|
"order": 22,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 29,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 31,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 35,
|
||||||
|
"round": 4,
|
||||||
|
"order": 24,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 32,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 33,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 36,
|
||||||
|
"round": 5,
|
||||||
|
"order": 25,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 34,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 35,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
401
brackets/13.json
Normal file
401
brackets/13.json
Normal file
@@ -0,0 +1,401 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 9,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_seed": 13,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5,
|
||||||
|
"away_seed": 12,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_seed": 10,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6,
|
||||||
|
"away_seed": 11,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 1,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 1,
|
||||||
|
"order": 8,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 2,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 1,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 1,
|
||||||
|
"order": 10,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 2,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 2,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 30,
|
||||||
|
"round": 3,
|
||||||
|
"order": 22,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 24,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 25,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 33,
|
||||||
|
"round": 4,
|
||||||
|
"order": 25,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 30,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 32,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 34,
|
||||||
|
"round": 5,
|
||||||
|
"order": 26,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 33,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 33,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 11,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 1,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": 11,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 2,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 5,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 7,
|
||||||
|
"home_from_loser": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 1,
|
||||||
|
"order": 12,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 11,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 1,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 1,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 1,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 2,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 27,
|
||||||
|
"round": 2,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 23,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 28,
|
||||||
|
"round": 3,
|
||||||
|
"order": 20,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 24,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 26,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 29,
|
||||||
|
"round": 3,
|
||||||
|
"order": 21,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 25,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 27,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 31,
|
||||||
|
"round": 4,
|
||||||
|
"order": 23,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 28,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 29,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 32,
|
||||||
|
"round": 5,
|
||||||
|
"order": 24,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 30,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 31,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
407
brackets/14.json
Normal file
407
brackets/14.json
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 9,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_seed": 13,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5,
|
||||||
|
"away_seed": 12,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_seed": 10,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_seed": 14,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6,
|
||||||
|
"away_seed": 11,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 1,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 1,
|
||||||
|
"order": 10,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 2,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 1,
|
||||||
|
"order": 11,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 1,
|
||||||
|
"order": 12,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 6,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 2,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 2,
|
||||||
|
"order": 20,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 30,
|
||||||
|
"round": 3,
|
||||||
|
"order": 25,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 24,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 25,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 33,
|
||||||
|
"round": 4,
|
||||||
|
"order": 28,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 30,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 32,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 34,
|
||||||
|
"round": 5,
|
||||||
|
"order": 29,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 33,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 33,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 11,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 1,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 2,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 5,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 0,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 6,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 1,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 11,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 1,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 1,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 1,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 19,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 2,
|
||||||
|
"order": 21,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 27,
|
||||||
|
"round": 2,
|
||||||
|
"order": 22,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 23,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 28,
|
||||||
|
"round": 3,
|
||||||
|
"order": 23,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 24,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 26,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 29,
|
||||||
|
"round": 3,
|
||||||
|
"order": 24,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 25,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 27,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 31,
|
||||||
|
"round": 4,
|
||||||
|
"order": 26,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 28,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 29,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 32,
|
||||||
|
"round": 5,
|
||||||
|
"order": 27,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 30,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 31,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
413
brackets/15.json
Normal file
413
brackets/15.json
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 9,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_seed": 13,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5,
|
||||||
|
"away_seed": 12,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_seed": 15,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_seed": 10,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_seed": 14,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6,
|
||||||
|
"away_seed": 11,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 1,
|
||||||
|
"order": 11,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 1,
|
||||||
|
"order": 12,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 2,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 1,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 4,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 1,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 6,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 2,
|
||||||
|
"order": 22,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 2,
|
||||||
|
"order": 23,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 30,
|
||||||
|
"round": 3,
|
||||||
|
"order": 28,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 24,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 25,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 33,
|
||||||
|
"round": 4,
|
||||||
|
"order": 31,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 30,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 32,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 34,
|
||||||
|
"round": 5,
|
||||||
|
"order": 32,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 33,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 33,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 11,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 1,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 2,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 0,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 4,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 0,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 6,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 1,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 11,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 1,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 1,
|
||||||
|
"order": 20,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 17,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 1,
|
||||||
|
"order": 21,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 19,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 2,
|
||||||
|
"order": 24,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 27,
|
||||||
|
"round": 2,
|
||||||
|
"order": 25,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 23,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 28,
|
||||||
|
"round": 3,
|
||||||
|
"order": 26,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 24,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 26,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 29,
|
||||||
|
"round": 3,
|
||||||
|
"order": 27,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 25,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 27,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 31,
|
||||||
|
"round": 4,
|
||||||
|
"order": 29,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 28,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 29,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 32,
|
||||||
|
"round": 5,
|
||||||
|
"order": 30,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 30,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 31,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
419
brackets/16.json
Normal file
419
brackets/16.json
Normal file
@@ -0,0 +1,419 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_seed": 16,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 9,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_seed": 13,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5,
|
||||||
|
"away_seed": 12,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_seed": 15,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_seed": 10,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_seed": 14,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": 8,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6,
|
||||||
|
"away_seed": 11,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 1,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 0,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 1,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 2,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 1,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 4,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 1,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 6,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 2,
|
||||||
|
"order": 25,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 2,
|
||||||
|
"order": 26,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 30,
|
||||||
|
"round": 3,
|
||||||
|
"order": 31,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 24,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 25,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 33,
|
||||||
|
"round": 4,
|
||||||
|
"order": 34,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 30,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 32,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 34,
|
||||||
|
"round": 5,
|
||||||
|
"order": 35,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 33,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 33,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 11,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 0,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 2,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 0,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 4,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 0,
|
||||||
|
"order": 20,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 6,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 1,
|
||||||
|
"order": 21,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 11,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 1,
|
||||||
|
"order": 22,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 1,
|
||||||
|
"order": 23,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 17,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 1,
|
||||||
|
"order": 24,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 19,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 2,
|
||||||
|
"order": 27,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 27,
|
||||||
|
"round": 2,
|
||||||
|
"order": 28,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 23,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 28,
|
||||||
|
"round": 3,
|
||||||
|
"order": 29,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 24,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 26,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 29,
|
||||||
|
"round": 3,
|
||||||
|
"order": 30,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 25,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 27,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 31,
|
||||||
|
"round": 4,
|
||||||
|
"order": 32,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 28,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 29,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 32,
|
||||||
|
"round": 5,
|
||||||
|
"order": 33,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 30,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 31,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
605
brackets/17.json
Normal file
605
brackets/17.json
Normal file
@@ -0,0 +1,605 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 16,
|
||||||
|
"away_seed": 17,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 13
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 11
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 1,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 1,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 9,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 1,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_seed": 13,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 1,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5,
|
||||||
|
"away_seed": 12,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 1,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_seed": 15,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 1,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_seed": 10,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 1,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_seed": 14,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 1,
|
||||||
|
"order": 8,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6,
|
||||||
|
"away_seed": 11,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 36,
|
||||||
|
"round": 2,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 17,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 37,
|
||||||
|
"round": 2,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 19,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 38,
|
||||||
|
"round": 2,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 39,
|
||||||
|
"round": 2,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 23,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 46,
|
||||||
|
"round": 3,
|
||||||
|
"order": 25,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 36,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 37,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 47,
|
||||||
|
"round": 3,
|
||||||
|
"order": 26,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 38,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 39,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 51,
|
||||||
|
"round": 4,
|
||||||
|
"order": 30,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 46,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 47,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 53,
|
||||||
|
"round": 5,
|
||||||
|
"order": 32,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 51,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 52,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 54,
|
||||||
|
"round": 6,
|
||||||
|
"order": 33,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 53,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 53,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 0,
|
||||||
|
"order": 10,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 27,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 28,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 29,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 30,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 17,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 31,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 32,
|
||||||
|
"round": 1,
|
||||||
|
"order": 11,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 25,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 33,
|
||||||
|
"round": 1,
|
||||||
|
"order": 12,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 20,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 34,
|
||||||
|
"round": 1,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 18,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 35,
|
||||||
|
"round": 1,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 17,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 16,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 40,
|
||||||
|
"round": 2,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 37,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 32,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 41,
|
||||||
|
"round": 2,
|
||||||
|
"order": 20,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 36,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 33,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 42,
|
||||||
|
"round": 2,
|
||||||
|
"order": 21,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 39,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 34,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 43,
|
||||||
|
"round": 2,
|
||||||
|
"order": 22,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 38,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 35,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 44,
|
||||||
|
"round": 3,
|
||||||
|
"order": 23,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 40,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 41,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 45,
|
||||||
|
"round": 3,
|
||||||
|
"order": 24,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 42,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 43,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 48,
|
||||||
|
"round": 4,
|
||||||
|
"order": 27,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 46,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 44,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 49,
|
||||||
|
"round": 4,
|
||||||
|
"order": 28,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 47,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 45,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 50,
|
||||||
|
"round": 5,
|
||||||
|
"order": 29,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 48,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 49,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 52,
|
||||||
|
"round": 6,
|
||||||
|
"order": 31,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 51,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 50,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
611
brackets/18.json
Normal file
611
brackets/18.json
Normal file
@@ -0,0 +1,611 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 16,
|
||||||
|
"away_seed": 17,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 13
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 0,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 15,
|
||||||
|
"away_seed": 18,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 11
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 1,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 1,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 9,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 1,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_seed": 13,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 1,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5,
|
||||||
|
"away_seed": 12,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 1,
|
||||||
|
"order": 10,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 1,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_seed": 10,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 1,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_seed": 14,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 1,
|
||||||
|
"order": 8,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6,
|
||||||
|
"away_seed": 11,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 36,
|
||||||
|
"round": 2,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 17,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 37,
|
||||||
|
"round": 2,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 19,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 38,
|
||||||
|
"round": 2,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 39,
|
||||||
|
"round": 2,
|
||||||
|
"order": 20,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 23,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 46,
|
||||||
|
"round": 3,
|
||||||
|
"order": 27,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 36,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 37,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 47,
|
||||||
|
"round": 3,
|
||||||
|
"order": 28,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 38,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 39,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 51,
|
||||||
|
"round": 4,
|
||||||
|
"order": 32,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 46,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 47,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 53,
|
||||||
|
"round": 5,
|
||||||
|
"order": 34,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 51,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 52,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 54,
|
||||||
|
"round": 6,
|
||||||
|
"order": 35,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 53,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 53,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 0,
|
||||||
|
"order": 11,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 27,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 28,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 29,
|
||||||
|
"round": 0,
|
||||||
|
"order": 12,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 30,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 17,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 31,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 32,
|
||||||
|
"round": 1,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 25,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 33,
|
||||||
|
"round": 1,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 20,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 34,
|
||||||
|
"round": 1,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 29,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 35,
|
||||||
|
"round": 1,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 17,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 16,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 40,
|
||||||
|
"round": 2,
|
||||||
|
"order": 21,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 37,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 32,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 41,
|
||||||
|
"round": 2,
|
||||||
|
"order": 22,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 36,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 33,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 42,
|
||||||
|
"round": 2,
|
||||||
|
"order": 23,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 39,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 34,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 43,
|
||||||
|
"round": 2,
|
||||||
|
"order": 24,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 38,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 35,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 44,
|
||||||
|
"round": 3,
|
||||||
|
"order": 25,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 40,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 41,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 45,
|
||||||
|
"round": 3,
|
||||||
|
"order": 26,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 42,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 43,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 48,
|
||||||
|
"round": 4,
|
||||||
|
"order": 29,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 46,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 44,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 49,
|
||||||
|
"round": 4,
|
||||||
|
"order": 30,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 47,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 45,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 50,
|
||||||
|
"round": 5,
|
||||||
|
"order": 31,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 48,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 49,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 52,
|
||||||
|
"round": 6,
|
||||||
|
"order": 33,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 51,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 50,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
617
brackets/19.json
Normal file
617
brackets/19.json
Normal file
@@ -0,0 +1,617 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 16,
|
||||||
|
"away_seed": 17,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 13
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 0,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 15,
|
||||||
|
"away_seed": 18,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 14,
|
||||||
|
"away_seed": 19,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 11
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 1,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 1,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 9,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 1,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_seed": 13,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 1,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5,
|
||||||
|
"away_seed": 12,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 1,
|
||||||
|
"order": 10,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 1,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_seed": 10,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 1,
|
||||||
|
"order": 11,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 1,
|
||||||
|
"order": 8,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6,
|
||||||
|
"away_seed": 11,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 36,
|
||||||
|
"round": 2,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 17,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 37,
|
||||||
|
"round": 2,
|
||||||
|
"order": 20,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 19,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 38,
|
||||||
|
"round": 2,
|
||||||
|
"order": 21,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 39,
|
||||||
|
"round": 2,
|
||||||
|
"order": 22,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 23,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 46,
|
||||||
|
"round": 3,
|
||||||
|
"order": 29,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 36,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 37,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 47,
|
||||||
|
"round": 3,
|
||||||
|
"order": 30,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 38,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 39,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 51,
|
||||||
|
"round": 4,
|
||||||
|
"order": 34,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 46,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 47,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 53,
|
||||||
|
"round": 5,
|
||||||
|
"order": 36,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 51,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 52,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 54,
|
||||||
|
"round": 6,
|
||||||
|
"order": 37,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 53,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 53,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 0,
|
||||||
|
"order": 12,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 27,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 28,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 29,
|
||||||
|
"round": 0,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 30,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 31,
|
||||||
|
"round": 0,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 17,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 32,
|
||||||
|
"round": 1,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 25,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 33,
|
||||||
|
"round": 1,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 20,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 34,
|
||||||
|
"round": 1,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 29,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 35,
|
||||||
|
"round": 1,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 31,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 40,
|
||||||
|
"round": 2,
|
||||||
|
"order": 23,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 37,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 32,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 41,
|
||||||
|
"round": 2,
|
||||||
|
"order": 24,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 36,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 33,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 42,
|
||||||
|
"round": 2,
|
||||||
|
"order": 25,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 39,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 34,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 43,
|
||||||
|
"round": 2,
|
||||||
|
"order": 26,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 38,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 35,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 44,
|
||||||
|
"round": 3,
|
||||||
|
"order": 27,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 40,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 41,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 45,
|
||||||
|
"round": 3,
|
||||||
|
"order": 28,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 42,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 43,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 48,
|
||||||
|
"round": 4,
|
||||||
|
"order": 31,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 46,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 44,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 49,
|
||||||
|
"round": 4,
|
||||||
|
"order": 32,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 47,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 45,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 50,
|
||||||
|
"round": 5,
|
||||||
|
"order": 33,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 48,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 49,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 52,
|
||||||
|
"round": 6,
|
||||||
|
"order": 35,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 51,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 50,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
623
brackets/20.json
Normal file
623
brackets/20.json
Normal file
@@ -0,0 +1,623 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 16,
|
||||||
|
"away_seed": 17,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 13,
|
||||||
|
"away_seed": 20,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 0,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 15,
|
||||||
|
"away_seed": 18,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 14,
|
||||||
|
"away_seed": 19,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 11
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 1,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 1,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 9,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 1,
|
||||||
|
"order": 10,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 1,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5,
|
||||||
|
"away_seed": 12,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 1,
|
||||||
|
"order": 11,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 1,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_seed": 10,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 1,
|
||||||
|
"order": 12,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 1,
|
||||||
|
"order": 8,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6,
|
||||||
|
"away_seed": 11,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 36,
|
||||||
|
"round": 2,
|
||||||
|
"order": 21,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 17,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 37,
|
||||||
|
"round": 2,
|
||||||
|
"order": 22,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 19,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 38,
|
||||||
|
"round": 2,
|
||||||
|
"order": 23,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 39,
|
||||||
|
"round": 2,
|
||||||
|
"order": 24,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 23,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 46,
|
||||||
|
"round": 3,
|
||||||
|
"order": 31,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 36,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 37,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 47,
|
||||||
|
"round": 3,
|
||||||
|
"order": 32,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 38,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 39,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 51,
|
||||||
|
"round": 4,
|
||||||
|
"order": 36,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 46,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 47,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 53,
|
||||||
|
"round": 5,
|
||||||
|
"order": 38,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 51,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 52,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 54,
|
||||||
|
"round": 6,
|
||||||
|
"order": 39,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 53,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 53,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 0,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 27,
|
||||||
|
"round": 0,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 28,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 29,
|
||||||
|
"round": 0,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 30,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 31,
|
||||||
|
"round": 0,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 17,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 32,
|
||||||
|
"round": 1,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 25,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 33,
|
||||||
|
"round": 1,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 27,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 34,
|
||||||
|
"round": 1,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 29,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 35,
|
||||||
|
"round": 1,
|
||||||
|
"order": 20,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 31,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 40,
|
||||||
|
"round": 2,
|
||||||
|
"order": 25,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 37,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 32,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 41,
|
||||||
|
"round": 2,
|
||||||
|
"order": 26,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 36,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 33,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 42,
|
||||||
|
"round": 2,
|
||||||
|
"order": 27,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 39,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 34,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 43,
|
||||||
|
"round": 2,
|
||||||
|
"order": 28,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 38,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 35,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 44,
|
||||||
|
"round": 3,
|
||||||
|
"order": 29,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 40,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 41,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 45,
|
||||||
|
"round": 3,
|
||||||
|
"order": 30,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 42,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 43,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 48,
|
||||||
|
"round": 4,
|
||||||
|
"order": 33,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 46,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 44,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 49,
|
||||||
|
"round": 4,
|
||||||
|
"order": 34,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 47,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 45,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 50,
|
||||||
|
"round": 5,
|
||||||
|
"order": 35,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 48,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 49,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 52,
|
||||||
|
"round": 6,
|
||||||
|
"order": 37,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 51,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 50,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
737
brackets/24.json
Normal file
737
brackets/24.json
Normal file
@@ -0,0 +1,737 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 16,
|
||||||
|
"away_seed": 17,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 9,
|
||||||
|
"away_seed": 24,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 13,
|
||||||
|
"away_seed": 20,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 12,
|
||||||
|
"away_seed": 21,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 0,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 15,
|
||||||
|
"away_seed": 18,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 0,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 10,
|
||||||
|
"away_seed": 23,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 14,
|
||||||
|
"away_seed": 19,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": 8,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 11,
|
||||||
|
"away_seed": 22,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 1,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 1,
|
||||||
|
"order": 10,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 1,
|
||||||
|
"order": 11,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 1,
|
||||||
|
"order": 12,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 1,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 1,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 1,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 1,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 56,
|
||||||
|
"round": 2,
|
||||||
|
"order": 33,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 17,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 57,
|
||||||
|
"round": 2,
|
||||||
|
"order": 34,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 19,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 58,
|
||||||
|
"round": 2,
|
||||||
|
"order": 35,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 59,
|
||||||
|
"round": 2,
|
||||||
|
"order": 36,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 23,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 68,
|
||||||
|
"round": 3,
|
||||||
|
"order": 45,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 56,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 57,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 69,
|
||||||
|
"round": 3,
|
||||||
|
"order": 46,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 58,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 59,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 74,
|
||||||
|
"round": 4,
|
||||||
|
"order": 51,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 68,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 69,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 77,
|
||||||
|
"round": 5,
|
||||||
|
"order": 54,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 74,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 76,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 78,
|
||||||
|
"round": 6,
|
||||||
|
"order": 55,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 77,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 77,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 40,
|
||||||
|
"round": 0,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 41,
|
||||||
|
"round": 0,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 42,
|
||||||
|
"round": 0,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 43,
|
||||||
|
"round": 0,
|
||||||
|
"order": 20,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 44,
|
||||||
|
"round": 0,
|
||||||
|
"order": 21,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 45,
|
||||||
|
"round": 0,
|
||||||
|
"order": 22,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 46,
|
||||||
|
"round": 0,
|
||||||
|
"order": 23,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 17,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 47,
|
||||||
|
"round": 0,
|
||||||
|
"order": 24,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 48,
|
||||||
|
"round": 1,
|
||||||
|
"order": 25,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 49,
|
||||||
|
"round": 1,
|
||||||
|
"order": 26,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 50,
|
||||||
|
"round": 1,
|
||||||
|
"order": 27,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 51,
|
||||||
|
"round": 1,
|
||||||
|
"order": 28,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 52,
|
||||||
|
"round": 1,
|
||||||
|
"order": 29,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 53,
|
||||||
|
"round": 1,
|
||||||
|
"order": 30,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 54,
|
||||||
|
"round": 1,
|
||||||
|
"order": 31,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 17,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 55,
|
||||||
|
"round": 1,
|
||||||
|
"order": 32,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 60,
|
||||||
|
"round": 2,
|
||||||
|
"order": 37,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 48,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 49,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 61,
|
||||||
|
"round": 2,
|
||||||
|
"order": 38,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 50,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 51,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 62,
|
||||||
|
"round": 2,
|
||||||
|
"order": 39,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 52,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 53,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 63,
|
||||||
|
"round": 2,
|
||||||
|
"order": 40,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 54,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 55,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 64,
|
||||||
|
"round": 3,
|
||||||
|
"order": 41,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 57,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 60,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 65,
|
||||||
|
"round": 3,
|
||||||
|
"order": 42,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 56,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 61,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 66,
|
||||||
|
"round": 3,
|
||||||
|
"order": 43,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 59,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 62,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 67,
|
||||||
|
"round": 3,
|
||||||
|
"order": 44,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 58,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 63,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 70,
|
||||||
|
"round": 4,
|
||||||
|
"order": 47,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 64,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 65,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 71,
|
||||||
|
"round": 4,
|
||||||
|
"order": 48,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 66,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 67,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 72,
|
||||||
|
"round": 5,
|
||||||
|
"order": 49,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 68,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 70,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 73,
|
||||||
|
"round": 5,
|
||||||
|
"order": 50,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 69,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 71,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 75,
|
||||||
|
"round": 6,
|
||||||
|
"order": 52,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 72,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 73,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 76,
|
||||||
|
"round": 7,
|
||||||
|
"order": 53,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 74,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 75,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
783
brackets/25.json
Normal file
783
brackets/25.json
Normal file
@@ -0,0 +1,783 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 16,
|
||||||
|
"away_seed": 17,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 25,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 9,
|
||||||
|
"away_seed": 24,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 13,
|
||||||
|
"away_seed": 20,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 12,
|
||||||
|
"away_seed": 21,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 0,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 15,
|
||||||
|
"away_seed": 18,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 0,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 10,
|
||||||
|
"away_seed": 23,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": 8,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 14,
|
||||||
|
"away_seed": 19,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 11,
|
||||||
|
"away_seed": 22,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 1,
|
||||||
|
"order": 11,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 1,
|
||||||
|
"order": 12,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 2,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 1,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 1,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 1,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 1,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 1,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 1,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 48,
|
||||||
|
"round": 2,
|
||||||
|
"order": 28,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 17,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 49,
|
||||||
|
"round": 2,
|
||||||
|
"order": 29,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 19,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 50,
|
||||||
|
"round": 2,
|
||||||
|
"order": 30,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 51,
|
||||||
|
"round": 2,
|
||||||
|
"order": 31,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 23,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 60,
|
||||||
|
"round": 3,
|
||||||
|
"order": 40,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 48,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 49,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 61,
|
||||||
|
"round": 3,
|
||||||
|
"order": 41,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 50,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 51,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 66,
|
||||||
|
"round": 4,
|
||||||
|
"order": 46,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 60,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 61,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 69,
|
||||||
|
"round": 5,
|
||||||
|
"order": 49,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 66,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 68,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 70,
|
||||||
|
"round": 6,
|
||||||
|
"order": 50,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 69,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 69,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 1,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 27,
|
||||||
|
"round": 0,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 2,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 28,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 29,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 5,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 30,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 31,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 7,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 32,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 33,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 34,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 35,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 11,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 36,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 17,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 37,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 13,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 38,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 39,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 15,
|
||||||
|
"home_from_loser": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 40,
|
||||||
|
"round": 1,
|
||||||
|
"order": 20,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 41,
|
||||||
|
"round": 1,
|
||||||
|
"order": 21,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 27,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 42,
|
||||||
|
"round": 1,
|
||||||
|
"order": 22,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 43,
|
||||||
|
"round": 1,
|
||||||
|
"order": 23,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 44,
|
||||||
|
"round": 1,
|
||||||
|
"order": 24,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 45,
|
||||||
|
"round": 1,
|
||||||
|
"order": 25,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 46,
|
||||||
|
"round": 1,
|
||||||
|
"order": 26,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 17,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 47,
|
||||||
|
"round": 1,
|
||||||
|
"order": 27,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 52,
|
||||||
|
"round": 2,
|
||||||
|
"order": 32,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 40,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 41,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 53,
|
||||||
|
"round": 2,
|
||||||
|
"order": 33,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 42,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 43,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 54,
|
||||||
|
"round": 2,
|
||||||
|
"order": 34,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 44,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 45,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 55,
|
||||||
|
"round": 2,
|
||||||
|
"order": 35,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 46,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 47,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 56,
|
||||||
|
"round": 3,
|
||||||
|
"order": 36,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 49,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 52,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 57,
|
||||||
|
"round": 3,
|
||||||
|
"order": 37,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 48,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 53,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 58,
|
||||||
|
"round": 3,
|
||||||
|
"order": 38,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 51,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 54,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 59,
|
||||||
|
"round": 3,
|
||||||
|
"order": 39,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 50,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 55,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 62,
|
||||||
|
"round": 4,
|
||||||
|
"order": 42,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 56,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 57,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 63,
|
||||||
|
"round": 4,
|
||||||
|
"order": 43,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 58,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 59,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 64,
|
||||||
|
"round": 5,
|
||||||
|
"order": 44,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 60,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 62,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 65,
|
||||||
|
"round": 5,
|
||||||
|
"order": 45,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 61,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 63,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 67,
|
||||||
|
"round": 6,
|
||||||
|
"order": 47,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 64,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 65,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 68,
|
||||||
|
"round": 7,
|
||||||
|
"order": 48,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 66,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 67,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
789
brackets/26.json
Normal file
789
brackets/26.json
Normal file
@@ -0,0 +1,789 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 16,
|
||||||
|
"away_seed": 17,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 25,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 9,
|
||||||
|
"away_seed": 24,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 13,
|
||||||
|
"away_seed": 20,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 12,
|
||||||
|
"away_seed": 21,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 0,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 15,
|
||||||
|
"away_seed": 18,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 0,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_seed": 26,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 0,
|
||||||
|
"order": 8,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 10,
|
||||||
|
"away_seed": 23,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 14,
|
||||||
|
"away_seed": 19,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": 10,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 11,
|
||||||
|
"away_seed": 22,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 1,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 1,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 2,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 1,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 1,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 1,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 1,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 1,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 1,
|
||||||
|
"order": 20,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 48,
|
||||||
|
"round": 2,
|
||||||
|
"order": 31,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 17,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 49,
|
||||||
|
"round": 2,
|
||||||
|
"order": 32,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 19,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 50,
|
||||||
|
"round": 2,
|
||||||
|
"order": 33,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 51,
|
||||||
|
"round": 2,
|
||||||
|
"order": 34,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 23,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 60,
|
||||||
|
"round": 3,
|
||||||
|
"order": 43,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 48,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 49,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 61,
|
||||||
|
"round": 3,
|
||||||
|
"order": 44,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 50,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 51,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 66,
|
||||||
|
"round": 4,
|
||||||
|
"order": 49,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 60,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 61,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 69,
|
||||||
|
"round": 5,
|
||||||
|
"order": 52,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 66,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 68,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 70,
|
||||||
|
"round": 6,
|
||||||
|
"order": 53,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 69,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 69,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 1,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 27,
|
||||||
|
"round": 0,
|
||||||
|
"order": 21,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 2,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 28,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 29,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 5,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 30,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 31,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 7,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 32,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 33,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 34,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 35,
|
||||||
|
"round": 0,
|
||||||
|
"order": 22,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 36,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 17,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 37,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 13,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 38,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 39,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 15,
|
||||||
|
"home_from_loser": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 40,
|
||||||
|
"round": 1,
|
||||||
|
"order": 23,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 41,
|
||||||
|
"round": 1,
|
||||||
|
"order": 24,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 27,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 42,
|
||||||
|
"round": 1,
|
||||||
|
"order": 25,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 43,
|
||||||
|
"round": 1,
|
||||||
|
"order": 26,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 44,
|
||||||
|
"round": 1,
|
||||||
|
"order": 27,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 45,
|
||||||
|
"round": 1,
|
||||||
|
"order": 28,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 35,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 46,
|
||||||
|
"round": 1,
|
||||||
|
"order": 29,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 17,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 47,
|
||||||
|
"round": 1,
|
||||||
|
"order": 30,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 52,
|
||||||
|
"round": 2,
|
||||||
|
"order": 35,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 40,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 41,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 53,
|
||||||
|
"round": 2,
|
||||||
|
"order": 36,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 42,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 43,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 54,
|
||||||
|
"round": 2,
|
||||||
|
"order": 37,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 44,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 45,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 55,
|
||||||
|
"round": 2,
|
||||||
|
"order": 38,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 46,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 47,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 56,
|
||||||
|
"round": 3,
|
||||||
|
"order": 39,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 49,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 52,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 57,
|
||||||
|
"round": 3,
|
||||||
|
"order": 40,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 48,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 53,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 58,
|
||||||
|
"round": 3,
|
||||||
|
"order": 41,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 51,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 54,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 59,
|
||||||
|
"round": 3,
|
||||||
|
"order": 42,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 50,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 55,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 62,
|
||||||
|
"round": 4,
|
||||||
|
"order": 45,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 56,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 57,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 63,
|
||||||
|
"round": 4,
|
||||||
|
"order": 46,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 58,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 59,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 64,
|
||||||
|
"round": 5,
|
||||||
|
"order": 47,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 60,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 62,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 65,
|
||||||
|
"round": 5,
|
||||||
|
"order": 48,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 61,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 63,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 67,
|
||||||
|
"round": 6,
|
||||||
|
"order": 50,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 64,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 65,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 68,
|
||||||
|
"round": 7,
|
||||||
|
"order": 51,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 66,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 67,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
795
brackets/27.json
Normal file
795
brackets/27.json
Normal file
@@ -0,0 +1,795 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 16,
|
||||||
|
"away_seed": 17,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 25,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 9,
|
||||||
|
"away_seed": 24,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 13,
|
||||||
|
"away_seed": 20,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 12,
|
||||||
|
"away_seed": 21,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 0,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 15,
|
||||||
|
"away_seed": 18,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 0,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7,
|
||||||
|
"away_seed": 26,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 0,
|
||||||
|
"order": 8,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 10,
|
||||||
|
"away_seed": 23,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 14,
|
||||||
|
"away_seed": 19,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": 10,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6,
|
||||||
|
"away_seed": 27,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": 11,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 11,
|
||||||
|
"away_seed": 22,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 1,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 1,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 2,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 1,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 1,
|
||||||
|
"order": 18,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 1,
|
||||||
|
"order": 19,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 1,
|
||||||
|
"order": 20,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 1,
|
||||||
|
"order": 21,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 1,
|
||||||
|
"order": 22,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 14,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 48,
|
||||||
|
"round": 2,
|
||||||
|
"order": 34,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 17,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 49,
|
||||||
|
"round": 2,
|
||||||
|
"order": 35,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 19,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 50,
|
||||||
|
"round": 2,
|
||||||
|
"order": 36,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 51,
|
||||||
|
"round": 2,
|
||||||
|
"order": 37,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 23,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 60,
|
||||||
|
"round": 3,
|
||||||
|
"order": 46,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 48,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 49,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 61,
|
||||||
|
"round": 3,
|
||||||
|
"order": 47,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 50,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 51,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 66,
|
||||||
|
"round": 4,
|
||||||
|
"order": 52,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 60,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 61,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 69,
|
||||||
|
"round": 5,
|
||||||
|
"order": 55,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 66,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 68,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 70,
|
||||||
|
"round": 6,
|
||||||
|
"order": 56,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 69,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 69,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 1,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 27,
|
||||||
|
"round": 0,
|
||||||
|
"order": 23,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 2,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 3,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 28,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 29,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 5,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 30,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 31,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 7,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 32,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 33,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 34,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 35,
|
||||||
|
"round": 0,
|
||||||
|
"order": 24,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 36,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 17,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 37,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 13,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 38,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 39,
|
||||||
|
"round": 0,
|
||||||
|
"order": 25,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 14,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 15,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 40,
|
||||||
|
"round": 1,
|
||||||
|
"order": 26,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 41,
|
||||||
|
"round": 1,
|
||||||
|
"order": 27,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 22,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 27,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 42,
|
||||||
|
"round": 1,
|
||||||
|
"order": 28,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 21,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 5,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 43,
|
||||||
|
"round": 1,
|
||||||
|
"order": 29,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 7,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 44,
|
||||||
|
"round": 1,
|
||||||
|
"order": 30,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 45,
|
||||||
|
"round": 1,
|
||||||
|
"order": 31,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 35,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 46,
|
||||||
|
"round": 1,
|
||||||
|
"order": 32,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 17,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 47,
|
||||||
|
"round": 1,
|
||||||
|
"order": 33,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 16,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 39,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 52,
|
||||||
|
"round": 2,
|
||||||
|
"order": 38,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 40,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 41,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 53,
|
||||||
|
"round": 2,
|
||||||
|
"order": 39,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 42,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 43,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 54,
|
||||||
|
"round": 2,
|
||||||
|
"order": 40,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 44,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 45,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 55,
|
||||||
|
"round": 2,
|
||||||
|
"order": 41,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 46,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 47,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 56,
|
||||||
|
"round": 3,
|
||||||
|
"order": 42,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 49,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 52,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 57,
|
||||||
|
"round": 3,
|
||||||
|
"order": 43,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 48,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 53,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 58,
|
||||||
|
"round": 3,
|
||||||
|
"order": 44,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 51,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 54,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 59,
|
||||||
|
"round": 3,
|
||||||
|
"order": 45,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 50,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 55,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 62,
|
||||||
|
"round": 4,
|
||||||
|
"order": 48,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 56,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 57,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 63,
|
||||||
|
"round": 4,
|
||||||
|
"order": 49,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 58,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 59,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 64,
|
||||||
|
"round": 5,
|
||||||
|
"order": 50,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 60,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 62,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 65,
|
||||||
|
"round": 5,
|
||||||
|
"order": 51,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 61,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 63,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 67,
|
||||||
|
"round": 6,
|
||||||
|
"order": 53,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 64,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 65,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 68,
|
||||||
|
"round": 7,
|
||||||
|
"order": 54,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 66,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 67,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
311
brackets/9.json
Normal file
311
brackets/9.json
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
{
|
||||||
|
"winners": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 0,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 1,
|
||||||
|
"round": 0,
|
||||||
|
"order": 1,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 8,
|
||||||
|
"away_seed": 9,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 2,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 3,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 4,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 5,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 6,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 7,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 8,
|
||||||
|
"round": 1,
|
||||||
|
"order": 5,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 1,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 9,
|
||||||
|
"round": 1,
|
||||||
|
"order": 2,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 4,
|
||||||
|
"away_seed": 5,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 10,
|
||||||
|
"round": 1,
|
||||||
|
"order": 3,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 2,
|
||||||
|
"away_seed": 7,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 11,
|
||||||
|
"round": 1,
|
||||||
|
"order": 4,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": null,
|
||||||
|
"home_from_loser": null,
|
||||||
|
"home_seed": 3,
|
||||||
|
"away_seed": 6,
|
||||||
|
"away_from_lid": null,
|
||||||
|
"away_from_loser": null,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 18,
|
||||||
|
"round": 2,
|
||||||
|
"order": 9,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 9,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 19,
|
||||||
|
"round": 2,
|
||||||
|
"order": 10,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 11,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 23,
|
||||||
|
"round": 3,
|
||||||
|
"order": 14,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 19,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 25,
|
||||||
|
"round": 4,
|
||||||
|
"order": 16,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 24,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 26,
|
||||||
|
"round": 5,
|
||||||
|
"order": 17,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 25,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 25,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"losers": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 12,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 13,
|
||||||
|
"round": 0,
|
||||||
|
"order": 6,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 11,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 1,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 14,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 15,
|
||||||
|
"round": 0,
|
||||||
|
"order": null,
|
||||||
|
"bye": true,
|
||||||
|
"home_from_lid": 8,
|
||||||
|
"home_from_loser": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 16,
|
||||||
|
"round": 1,
|
||||||
|
"order": 7,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 10,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 13,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 17,
|
||||||
|
"round": 1,
|
||||||
|
"order": 8,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 9,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 8,
|
||||||
|
"away_from_loser": true,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 20,
|
||||||
|
"round": 2,
|
||||||
|
"order": 11,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 18,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 16,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lid": 21,
|
||||||
|
"round": 2,
|
||||||
|
"order": 12,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 19,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 17,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 22,
|
||||||
|
"round": 3,
|
||||||
|
"order": 13,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 20,
|
||||||
|
"home_from_loser": false,
|
||||||
|
"away_from_lid": 21,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lid": 24,
|
||||||
|
"round": 4,
|
||||||
|
"order": 15,
|
||||||
|
"bye": false,
|
||||||
|
"home_from_lid": 23,
|
||||||
|
"home_from_loser": true,
|
||||||
|
"away_from_lid": 22,
|
||||||
|
"away_from_loser": false,
|
||||||
|
"reset": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -32,17 +32,17 @@ services:
|
|||||||
- app-network
|
- app-network
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
redis:
|
#redis:
|
||||||
image: redis:7-alpine
|
# image: redis:7-alpine
|
||||||
container_name: redis-cache
|
# container_name: redis-cache
|
||||||
ports:
|
# ports:
|
||||||
- "6379:6379"
|
# - "6379:6379"
|
||||||
command: redis-server --appendonly yes
|
# command: redis-server --appendonly yes
|
||||||
volumes:
|
# volumes:
|
||||||
- redis-data:/data
|
# - redis-data:/data
|
||||||
networks:
|
# networks:
|
||||||
- app-network
|
# - app-network
|
||||||
restart: unless-stopped
|
# restart: unless-stopped
|
||||||
|
|
||||||
supertokens:
|
supertokens:
|
||||||
image: registry.supertokens.io/supertokens/supertokens-postgresql
|
image: registry.supertokens.io/supertokens/supertokens-postgresql
|
||||||
@@ -51,6 +51,7 @@ services:
|
|||||||
- postgres
|
- postgres
|
||||||
environment:
|
environment:
|
||||||
POSTGRESQL_CONNECTION_URI: postgresql://supertokens:password@postgres:5432/supertokens
|
POSTGRESQL_CONNECTION_URI: postgresql://supertokens:password@postgres:5432/supertokens
|
||||||
|
ACCESS_TOKEN_VALIDITY: 360000
|
||||||
ports:
|
ports:
|
||||||
- "3567:3567"
|
- "3567:3567"
|
||||||
env_file:
|
env_file:
|
||||||
|
|||||||
18
package.json
18
package.json
@@ -6,7 +6,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev --host 0.0.0.0",
|
"dev": "vite dev --host 0.0.0.0",
|
||||||
"build": "vite build && tsc --noEmit",
|
"build": "vite build && tsc --noEmit",
|
||||||
"start": "vite start"
|
"start": "bun run .output/server/index.mjs",
|
||||||
|
"start:node": "node .output/server/index.mjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hello-pangea/dnd": "^18.0.1",
|
"@hello-pangea/dnd": "^18.0.1",
|
||||||
@@ -18,13 +19,21 @@
|
|||||||
"@mantine/hooks": "^8.2.4",
|
"@mantine/hooks": "^8.2.4",
|
||||||
"@mantine/tiptap": "^8.2.4",
|
"@mantine/tiptap": "^8.2.4",
|
||||||
"@phosphor-icons/react": "^2.1.10",
|
"@phosphor-icons/react": "^2.1.10",
|
||||||
|
"@svgmoji/noto": "^3.2.0",
|
||||||
"@tanstack/react-query": "^5.66.0",
|
"@tanstack/react-query": "^5.66.0",
|
||||||
"@tanstack/react-query-devtools": "^5.66.0",
|
"@tanstack/react-query-devtools": "^5.66.0",
|
||||||
"@tanstack/react-router": "^1.130.12",
|
"@tanstack/react-router": "^1.130.12",
|
||||||
"@tanstack/react-router-devtools": "^1.130.13",
|
"@tanstack/react-router-devtools": "^1.130.13",
|
||||||
"@tanstack/react-router-with-query": "^1.130.12",
|
"@tanstack/react-router-with-query": "^1.130.12",
|
||||||
"@tanstack/react-start": "^1.130.15",
|
"@tanstack/react-start": "^1.132.2",
|
||||||
|
"@tanstack/react-virtual": "^3.13.12",
|
||||||
|
"@tiptap/pm": "^3.4.3",
|
||||||
|
"@tiptap/react": "^3.4.3",
|
||||||
|
"@tiptap/starter-kit": "^3.4.3",
|
||||||
|
"@types/bun": "^1.2.22",
|
||||||
"@types/ioredis": "^4.28.10",
|
"@types/ioredis": "^4.28.10",
|
||||||
|
"browser-image-compression": "^2.0.2",
|
||||||
|
"dotenv": "^17.2.2",
|
||||||
"embla-carousel-react": "^8.6.0",
|
"embla-carousel-react": "^8.6.0",
|
||||||
"framer-motion": "^12.23.12",
|
"framer-motion": "^12.23.12",
|
||||||
"ioredis": "^5.7.0",
|
"ioredis": "^5.7.0",
|
||||||
@@ -33,6 +42,7 @@
|
|||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-imask": "^7.6.1",
|
"react-imask": "^7.6.1",
|
||||||
|
"react-scan": "^0.4.3",
|
||||||
"react-use-draggable-scroll": "^0.4.7",
|
"react-use-draggable-scroll": "^0.4.7",
|
||||||
"recharts": "^3.1.2",
|
"recharts": "^3.1.2",
|
||||||
"redaxios": "^0.5.1",
|
"redaxios": "^0.5.1",
|
||||||
@@ -45,6 +55,8 @@
|
|||||||
"zustand": "^5.0.7"
|
"zustand": "^5.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@tanstack/react-router-ssr-query": "^1.132.2",
|
||||||
|
"@tanstack/router-plugin": "^1.132.2",
|
||||||
"@types/node": "^22.5.4",
|
"@types/node": "^22.5.4",
|
||||||
"@types/pg": "^8.15.5",
|
"@types/pg": "^8.15.5",
|
||||||
"@types/react": "^19.0.8",
|
"@types/react": "^19.0.8",
|
||||||
@@ -57,7 +69,7 @@
|
|||||||
"postcss-simple-vars": "^7.0.1",
|
"postcss-simple-vars": "^7.0.1",
|
||||||
"tsx": "^4.20.3",
|
"tsx": "^4.20.3",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.2",
|
||||||
"vite": "^6.3.5",
|
"vite": "^7.1.7",
|
||||||
"vite-tsconfig-paths": "^5.1.4"
|
"vite-tsconfig-paths": "^5.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
44
pb_migrations/1757211840_updated_matches.js
Normal file
44
pb_migrations/1757211840_updated_matches.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2541054544")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(18, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number1705071305",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "home_seed",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(19, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number3588777624",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "away_seed",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2541054544")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("number1705071305")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("number3588777624")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
28
pb_migrations/1757211934_updated_tournaments.js
Normal file
28
pb_migrations/1757211934_updated_tournaments.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_340646327")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(10, new Field({
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_2541054544",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation103159226",
|
||||||
|
"maxSelect": 999,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "matches",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_340646327")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("relation103159226")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
27
pb_migrations/1757263183_updated_matches.js
Normal file
27
pb_migrations/1757263183_updated_matches.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2541054544")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(20, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number3320769076",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "round",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2541054544")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("number3320769076")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
28
pb_migrations/1757386414_updated_players.js
Normal file
28
pb_migrations/1757386414_updated_players.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3072146508")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"createRule": "",
|
||||||
|
"deleteRule": "",
|
||||||
|
"listRule": "",
|
||||||
|
"updateRule": "",
|
||||||
|
"viewRule": ""
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3072146508")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"listRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
28
pb_migrations/1757386423_updated_matches.js
Normal file
28
pb_migrations/1757386423_updated_matches.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2541054544")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"createRule": "",
|
||||||
|
"deleteRule": "",
|
||||||
|
"listRule": "",
|
||||||
|
"updateRule": "",
|
||||||
|
"viewRule": ""
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2541054544")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"listRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
28
pb_migrations/1757386431_updated_teams.js
Normal file
28
pb_migrations/1757386431_updated_teams.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1568971955")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"createRule": "",
|
||||||
|
"deleteRule": "",
|
||||||
|
"listRule": "",
|
||||||
|
"updateRule": "",
|
||||||
|
"viewRule": ""
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1568971955")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"listRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
28
pb_migrations/1757386438_updated_tournaments.js
Normal file
28
pb_migrations/1757386438_updated_tournaments.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_340646327")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"createRule": "",
|
||||||
|
"deleteRule": "",
|
||||||
|
"listRule": "",
|
||||||
|
"updateRule": "",
|
||||||
|
"viewRule": ""
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_340646327")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"listRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
31
pb_migrations/1757615830_updated_matches.js
Normal file
31
pb_migrations/1757615830_updated_matches.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2541054544")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(21, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "select2063623452",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"name": "status",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "select",
|
||||||
|
"values": [
|
||||||
|
"tbd",
|
||||||
|
"ready",
|
||||||
|
"started",
|
||||||
|
"ended"
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2541054544")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("select2063623452")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
235
pb_migrations/1757710506_created_player_stats.js
Normal file
235
pb_migrations/1757710506_created_player_stats.js
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_3072146508",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation2582050271",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "player_id",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json4231605813",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "player_name",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_976G",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "first_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_njLe",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "last_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_1568971955",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation694999214",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "team_id",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_ZNMy",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "team_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_340646327",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation869376999",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "tournament_id",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_nxTv",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "tournament_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number103159226",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "matches",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json2732118329",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "wins",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json724428801",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "losses",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3041953980",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "margin_of_victory",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json1531431708",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "margin_of_loss",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json1062535948",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_won_by",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json4249694556",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_lost_by",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3154249934",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_made",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3227208027",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_against",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_135889471",
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "player_stats",
|
||||||
|
"system": false,
|
||||||
|
"type": "view",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewQuery": "\n SELECT\n (p.id || '_' || t.id || '_' || tour.id) as id,\n p.id as player_id,\n (p.first_name || ' ' || p.last_name) as player_name,\n p.first_name,\n p.last_name,\n t.id as team_id,\n t.name as team_name,\n tour.id as tournament_id,\n tour.name as tournament_name,\n COUNT(m.id) as matches,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) as wins,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups < m.away_cups) OR\n (m.away = t.id AND m.away_cups < m.home_cups)\n THEN 1 ELSE 0\n END) as losses,\n AVG(CASE\n WHEN m.home = t.id AND m.home_cups > m.away_cups\n THEN m.home_cups - m.away_cups\n WHEN m.away = t.id AND m.away_cups > m.home_cups\n THEN m.away_cups - m.home_cups\n ELSE NULL\n END) as margin_of_victory,\n AVG(CASE\n WHEN m.home = t.id AND m.home_cups < m.away_cups\n THEN m.away_cups - m.home_cups\n WHEN m.away = t.id AND m.away_cups < m.home_cups\n THEN m.home_cups - m.away_cups\n ELSE NULL\n END) as margin_of_loss,\n SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) as total_cups_won_by,\n SUM(CASE\n WHEN m.home = t.id THEN m.away_cups\n WHEN m.away = t.id THEN m.home_cups\n ELSE 0\n END) as total_cups_lost_by,\n SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) as total_cups_made,\n SUM(CASE\n WHEN m.home = t.id THEN m.away_cups\n WHEN m.away = t.id THEN m.home_cups\n ELSE 0\n END) as total_cups_against\n FROM players p\n JOIN teams t ON json_extract(t.players, '$[*]') LIKE '%' || p.id || '%'\n JOIN matches m ON (m.home = t.id OR m.away = t.id)\n JOIN tournaments tour ON m.tournament = tour.id\n WHERE m.status = 'ended'\n GROUP BY p.id, t.id, tour.id",
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_135889471");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
})
|
||||||
194
pb_migrations/1757710693_updated_player_stats.js
Normal file
194
pb_migrations/1757710693_updated_player_stats.js
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_135889471")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"viewQuery": "SELECT\n (p.id || '_' || t.id || '_' || tour.id) as id,\n p.id as player_id,\n (p.first_name || ' ' || p.last_name) as player_name,\n t.id as team_id,\n t.name as team_name,\n tour.id as tournament_id,\n tour.name as tournament_name,\n COUNT(m.id) as matches,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) as wins,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups < m.away_cups) OR\n (m.away = t.id AND m.away_cups < m.home_cups)\n THEN 1 ELSE 0\n END) as losses,\n SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) as total_cups_made,\n SUM(CASE\n WHEN m.home = t.id THEN m.away_cups\n WHEN m.away = t.id THEN m.home_cups\n ELSE 0\n END) as total_cups_against\n FROM players p, teams t, matches m, tournaments tour\n WHERE\n t.players LIKE '%\"' || p.id || '\"%' AND\n (m.home = t.id OR m.away = t.id) AND\n m.tournament = tour.id AND\n m.status = 'ended'\n GROUP BY p.id, t.id, tour.id"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_976G")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_njLe")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_ZNMy")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_nxTv")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("json3041953980")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("json1531431708")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("json1062535948")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("json4249694556")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(4, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_YqC8",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "team_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(6, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_jZTo",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "tournament_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_135889471")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"viewQuery": "\n SELECT\n (p.id || '_' || t.id || '_' || tour.id) as id,\n p.id as player_id,\n (p.first_name || ' ' || p.last_name) as player_name,\n p.first_name,\n p.last_name,\n t.id as team_id,\n t.name as team_name,\n tour.id as tournament_id,\n tour.name as tournament_name,\n COUNT(m.id) as matches,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) as wins,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups < m.away_cups) OR\n (m.away = t.id AND m.away_cups < m.home_cups)\n THEN 1 ELSE 0\n END) as losses,\n AVG(CASE\n WHEN m.home = t.id AND m.home_cups > m.away_cups\n THEN m.home_cups - m.away_cups\n WHEN m.away = t.id AND m.away_cups > m.home_cups\n THEN m.away_cups - m.home_cups\n ELSE NULL\n END) as margin_of_victory,\n AVG(CASE\n WHEN m.home = t.id AND m.home_cups < m.away_cups\n THEN m.away_cups - m.home_cups\n WHEN m.away = t.id AND m.away_cups < m.home_cups\n THEN m.home_cups - m.away_cups\n ELSE NULL\n END) as margin_of_loss,\n SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) as total_cups_won_by,\n SUM(CASE\n WHEN m.home = t.id THEN m.away_cups\n WHEN m.away = t.id THEN m.home_cups\n ELSE 0\n END) as total_cups_lost_by,\n SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) as total_cups_made,\n SUM(CASE\n WHEN m.home = t.id THEN m.away_cups\n WHEN m.away = t.id THEN m.home_cups\n ELSE 0\n END) as total_cups_against\n FROM players p\n JOIN teams t ON json_extract(t.players, '$[*]') LIKE '%' || p.id || '%'\n JOIN matches m ON (m.home = t.id OR m.away = t.id)\n JOIN tournaments tour ON m.tournament = tour.id\n WHERE m.status = 'ended'\n GROUP BY p.id, t.id, tour.id"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(3, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_976G",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "first_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(4, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_njLe",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "last_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(6, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_ZNMy",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "team_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(8, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_nxTv",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "tournament_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(12, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3041953980",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "margin_of_victory",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(13, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json1531431708",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "margin_of_loss",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(14, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json1062535948",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_won_by",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(15, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json4249694556",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_lost_by",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_YqC8")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_jZTo")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
96
pb_migrations/1757710817_updated_player_stats.js
Normal file
96
pb_migrations/1757710817_updated_player_stats.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_135889471")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"name": "player_stats_per_tournament"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_YqC8")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_jZTo")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(4, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_XGbN",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "team_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(6, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_uud6",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "tournament_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_135889471")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"name": "player_stats"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(4, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_YqC8",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "team_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(6, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_jZTo",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "tournament_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_XGbN")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_uud6")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
180
pb_migrations/1757710882_created_player_stats.js
Normal file
180
pb_migrations/1757710882_created_player_stats.js
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_3072146508",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation2582050271",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "player_id",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json4231605813",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "player_name",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_1568971955",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation694999214",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "team_id",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_fQu1",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "team_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number103159226",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "matches",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json2732118329",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "wins",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json724428801",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "losses",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3154249934",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_made",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3227208027",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_against",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json2379943496",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "win_percentage",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3165107022",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "avg_cups_per_match",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3041953980",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "margin_of_victory",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json1531431708",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "margin_of_loss",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_1358894712",
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "player_stats",
|
||||||
|
"system": false,
|
||||||
|
"type": "view",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewQuery": "SELECT\n (p.id || '_' || t.id) as id,\n p.id as player_id,\n (p.first_name || ' ' || p.last_name) as player_name,\n t.id as team_id,\n t.name as team_name,\n COUNT(m.id) as matches,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) as wins,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups < m.away_cups) OR\n (m.away = t.id AND m.away_cups < m.home_cups)\n THEN 1 ELSE 0\n END) as losses,\n SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) as total_cups_made,\n SUM(CASE\n WHEN m.home = t.id THEN m.away_cups\n WHEN m.away = t.id THEN m.home_cups\n ELSE 0\n END) as total_cups_against,\n -- Additional calculated stats\n ROUND((CAST(SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) AS REAL) / COUNT(m.id)) * 100, 2) as win_percentage,\n ROUND(CAST(SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) AS REAL) / COUNT(m.id), 2) as avg_cups_per_match,\n -- Margin calculations\n AVG(CASE\n WHEN m.home = t.id AND m.home_cups > m.away_cups\n THEN m.home_cups - m.away_cups\n WHEN m.away = t.id AND m.away_cups > m.home_cups\n THEN m.away_cups - m.home_cups\n ELSE NULL\n END) as margin_of_victory,\n AVG(CASE\n WHEN m.home = t.id AND m.home_cups < m.away_cups\n THEN m.away_cups - m.home_cups\n WHEN m.away = t.id AND m.away_cups < m.home_cups\n THEN m.home_cups - m.away_cups\n ELSE NULL\n END) as margin_of_loss\n FROM players p, teams t, matches m, tournaments tour\n WHERE\n t.players LIKE '%\"' || p.id || '\"%' AND\n (m.home = t.id OR m.away = t.id) AND\n m.tournament = tour.id AND\n m.status = 'ended'\n GROUP BY p.id, t.id",
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1358894712");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
})
|
||||||
57
pb_migrations/1757711182_updated_player_stats.js
Normal file
57
pb_migrations/1757711182_updated_player_stats.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1358894712")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"viewQuery": "SELECT\n p.id as id,\n p.id as player_id,\n (p.first_name || ' ' || p.last_name) as player_name,\n COUNT(m.id) as matches,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) as wins,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups < m.away_cups) OR\n (m.away = t.id AND m.away_cups < m.home_cups)\n THEN 1 ELSE 0\n END) as losses,\n SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) as total_cups_made,\n SUM(CASE\n WHEN m.home = t.id THEN m.away_cups\n WHEN m.away = t.id THEN m.home_cups\n ELSE 0\n END) as total_cups_against,\n -- Win percentage\n ROUND((CAST(SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) AS REAL) / COUNT(m.id)) * 100, 2) as win_percentage,\n -- Average cups per match\n ROUND(CAST(SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) AS REAL) / COUNT(m.id), 2) as avg_cups_per_match,\n -- Margin of Victory\n ROUND(AVG(CASE\n WHEN m.home = t.id AND m.home_cups > m.away_cups\n THEN m.home_cups - m.away_cups\n WHEN m.away = t.id AND m.away_cups > m.home_cups\n THEN m.away_cups - m.home_cups\n ELSE NULL\n END), 2) as margin_of_victory,\n -- Margin of Loss\n ROUND(AVG(CASE\n WHEN m.home = t.id AND m.home_cups < m.away_cups\n THEN m.away_cups - m.home_cups\n WHEN m.away = t.id AND m.away_cups < m.home_cups\n THEN m.home_cups - m.away_cups\n ELSE NULL\n END), 2) as margin_of_loss\n FROM players p, teams t, matches m, tournaments tour\n WHERE\n t.players LIKE '%\"' || p.id || '\"%' AND\n (m.home = t.id OR m.away = t.id) AND\n m.tournament = tour.id AND\n m.status = 'ended'\n GROUP BY p.id"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("relation694999214")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_fQu1")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1358894712")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"viewQuery": "SELECT\n (p.id || '_' || t.id) as id,\n p.id as player_id,\n (p.first_name || ' ' || p.last_name) as player_name,\n t.id as team_id,\n t.name as team_name,\n COUNT(m.id) as matches,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) as wins,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups < m.away_cups) OR\n (m.away = t.id AND m.away_cups < m.home_cups)\n THEN 1 ELSE 0\n END) as losses,\n SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) as total_cups_made,\n SUM(CASE\n WHEN m.home = t.id THEN m.away_cups\n WHEN m.away = t.id THEN m.home_cups\n ELSE 0\n END) as total_cups_against,\n -- Additional calculated stats\n ROUND((CAST(SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) AS REAL) / COUNT(m.id)) * 100, 2) as win_percentage,\n ROUND(CAST(SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) AS REAL) / COUNT(m.id), 2) as avg_cups_per_match,\n -- Margin calculations\n AVG(CASE\n WHEN m.home = t.id AND m.home_cups > m.away_cups\n THEN m.home_cups - m.away_cups\n WHEN m.away = t.id AND m.away_cups > m.home_cups\n THEN m.away_cups - m.home_cups\n ELSE NULL\n END) as margin_of_victory,\n AVG(CASE\n WHEN m.home = t.id AND m.home_cups < m.away_cups\n THEN m.away_cups - m.home_cups\n WHEN m.away = t.id AND m.away_cups < m.home_cups\n THEN m.home_cups - m.away_cups\n ELSE NULL\n END) as margin_of_loss\n FROM players p, teams t, matches m, tournaments tour\n WHERE\n t.players LIKE '%\"' || p.id || '\"%' AND\n (m.home = t.id OR m.away = t.id) AND\n m.tournament = tour.id AND\n m.status = 'ended'\n GROUP BY p.id, t.id"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(3, new Field({
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_1568971955",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation694999214",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "team_id",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(4, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_fQu1",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "team_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
211
pb_migrations/1757800000_created_team_stats.js
Normal file
211
pb_migrations/1757800000_created_team_stats.js
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_1568971955",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation694999214",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "team_id",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_ZNMy",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "team_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number103159226",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "matches",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json2732118329",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "wins",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json724428801",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "losses",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3041953980",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "margin_of_victory",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json1531431708",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "margin_of_loss",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json1062535948",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_won_by",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json4249694556",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_lost_by",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3154249934",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_made",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3227208027",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_against",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_135889472",
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "team_stats",
|
||||||
|
"system": false,
|
||||||
|
"type": "view",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewQuery": `
|
||||||
|
SELECT
|
||||||
|
t.id as id,
|
||||||
|
t.id as team_id,
|
||||||
|
t.name as team_name,
|
||||||
|
COUNT(m.id) as matches,
|
||||||
|
SUM(CASE
|
||||||
|
WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR
|
||||||
|
(m.away = t.id AND m.away_cups > m.home_cups)
|
||||||
|
THEN 1 ELSE 0
|
||||||
|
END) as wins,
|
||||||
|
SUM(CASE
|
||||||
|
WHEN (m.home = t.id AND m.home_cups < m.away_cups) OR
|
||||||
|
(m.away = t.id AND m.away_cups < m.home_cups)
|
||||||
|
THEN 1 ELSE 0
|
||||||
|
END) as losses,
|
||||||
|
AVG(CASE
|
||||||
|
WHEN m.home = t.id AND m.home_cups > m.away_cups
|
||||||
|
THEN m.home_cups - m.away_cups
|
||||||
|
WHEN m.away = t.id AND m.away_cups > m.home_cups
|
||||||
|
THEN m.away_cups - m.home_cups
|
||||||
|
ELSE NULL
|
||||||
|
END) as margin_of_victory,
|
||||||
|
AVG(CASE
|
||||||
|
WHEN m.home = t.id AND m.home_cups < m.away_cups
|
||||||
|
THEN m.away_cups - m.home_cups
|
||||||
|
WHEN m.away = t.id AND m.away_cups < m.home_cups
|
||||||
|
THEN m.home_cups - m.away_cups
|
||||||
|
ELSE NULL
|
||||||
|
END) as margin_of_loss,
|
||||||
|
SUM(CASE
|
||||||
|
WHEN m.home = t.id THEN m.home_cups
|
||||||
|
WHEN m.away = t.id THEN m.away_cups
|
||||||
|
ELSE 0
|
||||||
|
END) as total_cups_won_by,
|
||||||
|
SUM(CASE
|
||||||
|
WHEN m.home = t.id THEN m.away_cups
|
||||||
|
WHEN m.away = t.id THEN m.home_cups
|
||||||
|
ELSE 0
|
||||||
|
END) as total_cups_lost_by,
|
||||||
|
SUM(CASE
|
||||||
|
WHEN m.home = t.id THEN m.home_cups
|
||||||
|
WHEN m.away = t.id THEN m.away_cups
|
||||||
|
ELSE 0
|
||||||
|
END) as total_cups_made,
|
||||||
|
SUM(CASE
|
||||||
|
WHEN m.home = t.id THEN m.away_cups
|
||||||
|
WHEN m.away = t.id THEN m.home_cups
|
||||||
|
ELSE 0
|
||||||
|
END) as total_cups_against
|
||||||
|
FROM teams t
|
||||||
|
JOIN matches m ON (m.home = t.id OR m.away = t.id)
|
||||||
|
JOIN tournaments tour ON m.tournament = tour.id
|
||||||
|
WHERE m.status = 'ended'
|
||||||
|
GROUP BY t.id`,
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_135889472");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
})
|
||||||
167
pb_migrations/1757910262_deleted_player_stats_per_tournament.js
Normal file
167
pb_migrations/1757910262_deleted_player_stats_per_tournament.js
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_135889471");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_3072146508",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation2582050271",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "player_id",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json4231605813",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "player_name",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_1568971955",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation694999214",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "team_id",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_XGbN",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "team_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_340646327",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation869376999",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "tournament_id",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_uud6",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "tournament_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number103159226",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "matches",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json2732118329",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "wins",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json724428801",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "losses",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3154249934",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_made",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3227208027",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_against",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_135889471",
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "player_stats_per_tournament",
|
||||||
|
"system": false,
|
||||||
|
"type": "view",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewQuery": "SELECT\n (p.id || '_' || t.id || '_' || tour.id) as id,\n p.id as player_id,\n (p.first_name || ' ' || p.last_name) as player_name,\n t.id as team_id,\n t.name as team_name,\n tour.id as tournament_id,\n tour.name as tournament_name,\n COUNT(m.id) as matches,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) as wins,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups < m.away_cups) OR\n (m.away = t.id AND m.away_cups < m.home_cups)\n THEN 1 ELSE 0\n END) as losses,\n SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) as total_cups_made,\n SUM(CASE\n WHEN m.home = t.id THEN m.away_cups\n WHEN m.away = t.id THEN m.home_cups\n ELSE 0\n END) as total_cups_against\n FROM players p, teams t, matches m, tournaments tour\n WHERE\n t.players LIKE '%\"' || p.id || '\"%' AND\n (m.home = t.id OR m.away = t.id) AND\n m.tournament = tour.id AND\n m.status = 'ended'\n GROUP BY p.id, t.id, tour.id",
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
})
|
||||||
96
pb_migrations/1757950535_updated_team_stats.js
Normal file
96
pb_migrations/1757950535_updated_team_stats.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_135889472")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"name": "team_stats_per_tournament"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_2Mic")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_C8ev")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(2, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_QmWG",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "team_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(4, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_1o7N",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "tournament_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_135889472")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"name": "team_stats"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(2, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_2Mic",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "team_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(4, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_C8ev",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "tournament_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_QmWG")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_1o7N")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
157
pb_migrations/1757950649_created_team_stats.js
Normal file
157
pb_migrations/1757950649_created_team_stats.js
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_1568971955",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation694999214",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "team_id",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_nYJn",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "team_name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number103159226",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "matches",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json2732118329",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "wins",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json724428801",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "losses",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3041953980",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "margin_of_victory",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json1531431708",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "margin_of_loss",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json1062535948",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_won_by",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json4249694556",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_lost_by",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3154249934",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_made",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3227208027",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "total_cups_against",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_1582517110",
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "team_stats",
|
||||||
|
"system": false,
|
||||||
|
"type": "view",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewQuery": "\n SELECT\n t.id as id,\n t.id as team_id,\n t.name as team_name,\n COUNT(m.id) as matches,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) as wins,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups < m.away_cups) OR\n (m.away = t.id AND m.away_cups < m.home_cups)\n THEN 1 ELSE 0\n END) as losses,\n AVG(CASE\n WHEN m.home = t.id AND m.home_cups > m.away_cups\n THEN m.home_cups - m.away_cups\n WHEN m.away = t.id AND m.away_cups > m.home_cups\n THEN m.away_cups - m.home_cups\n ELSE NULL\n END) as margin_of_victory,\n AVG(CASE\n WHEN m.home = t.id AND m.home_cups < m.away_cups\n THEN m.away_cups - m.home_cups\n WHEN m.away = t.id AND m.away_cups < m.home_cups\n THEN m.home_cups - m.away_cups\n ELSE NULL\n END) as margin_of_loss,\n SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) as total_cups_won_by,\n SUM(CASE\n WHEN m.home = t.id THEN m.away_cups\n WHEN m.away = t.id THEN m.home_cups\n ELSE 0\n END) as total_cups_lost_by,\n SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) as total_cups_made,\n SUM(CASE\n WHEN m.home = t.id THEN m.away_cups\n WHEN m.away = t.id THEN m.home_cups\n ELSE 0\n END) as total_cups_against\n FROM teams t\n JOIN matches m ON (m.home = t.id OR m.away = t.id)\n WHERE m.status = 'ended'\n GROUP BY t.id",
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1582517110");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
})
|
||||||
55
pb_migrations/1758042930_updated_teams.js
Normal file
55
pb_migrations/1758042930_updated_teams.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1568971955")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("number3356599746")
|
||||||
|
|
||||||
|
// update field
|
||||||
|
collection.fields.addAt(9, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number1329349942",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "song_end",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1568971955")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(8, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number3356599746",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "song_year",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// update field
|
||||||
|
collection.fields.addAt(10, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number1329349942",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "song_duration",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
37
pb_migrations/1758049631_updated_player_stats.js
Normal file
37
pb_migrations/1758049631_updated_player_stats.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1358894712")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"viewQuery": "SELECT\n p.id as id,\n p.id as player_id,\n (p.first_name || ' ' || p.last_name) as player_name,\n COUNT(m.id) as matches,\n COUNT(DISTINCT m.tournament) as tournaments,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) as wins,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups < m.away_cups) OR\n (m.away = t.id AND m.away_cups < m.home_cups)\n THEN 1 ELSE 0\n END) as losses,\n SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) as total_cups_made,\n SUM(CASE\n WHEN m.home = t.id THEN m.away_cups\n WHEN m.away = t.id THEN m.home_cups\n ELSE 0\n END) as total_cups_against,\n -- Win percentage\n ROUND((CAST(SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) AS REAL) / COUNT(m.id)) * 100, 2) as win_percentage,\n -- Average cups per match\n ROUND(CAST(SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) AS REAL) / COUNT(m.id), 2) as avg_cups_per_match,\n -- Margin of Victory\n ROUND(AVG(CASE\n WHEN m.home = t.id AND m.home_cups > m.away_cups\n THEN m.home_cups - m.away_cups\n WHEN m.away = t.id AND m.away_cups > m.home_cups\n THEN m.away_cups - m.home_cups\n ELSE NULL\n END), 2) as margin_of_victory,\n -- Margin of Loss\n ROUND(AVG(CASE\n WHEN m.home = t.id AND m.home_cups < m.away_cups\n THEN m.away_cups - m.home_cups\n WHEN m.away = t.id AND m.away_cups < m.home_cups\n THEN m.home_cups - m.away_cups\n ELSE NULL\n END), 2) as margin_of_loss\n FROM players p, teams t, matches m, tournaments tour\n WHERE\n t.players LIKE '%\"' || p.id || '\"%' AND\n (m.home = t.id OR m.away = t.id) AND\n m.tournament = tour.id AND\n m.status = 'ended'\n GROUP BY p.id"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(4, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number3837590211",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "tournaments",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1358894712")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"viewQuery": "SELECT\n p.id as id,\n p.id as player_id,\n (p.first_name || ' ' || p.last_name) as player_name,\n COUNT(m.id) as matches,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) as wins,\n SUM(CASE\n WHEN (m.home = t.id AND m.home_cups < m.away_cups) OR\n (m.away = t.id AND m.away_cups < m.home_cups)\n THEN 1 ELSE 0\n END) as losses,\n SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) as total_cups_made,\n SUM(CASE\n WHEN m.home = t.id THEN m.away_cups\n WHEN m.away = t.id THEN m.home_cups\n ELSE 0\n END) as total_cups_against,\n -- Win percentage\n ROUND((CAST(SUM(CASE\n WHEN (m.home = t.id AND m.home_cups > m.away_cups) OR\n (m.away = t.id AND m.away_cups > m.home_cups)\n THEN 1 ELSE 0\n END) AS REAL) / COUNT(m.id)) * 100, 2) as win_percentage,\n -- Average cups per match\n ROUND(CAST(SUM(CASE\n WHEN m.home = t.id THEN m.home_cups\n WHEN m.away = t.id THEN m.away_cups\n ELSE 0\n END) AS REAL) / COUNT(m.id), 2) as avg_cups_per_match,\n -- Margin of Victory\n ROUND(AVG(CASE\n WHEN m.home = t.id AND m.home_cups > m.away_cups\n THEN m.home_cups - m.away_cups\n WHEN m.away = t.id AND m.away_cups > m.home_cups\n THEN m.away_cups - m.home_cups\n ELSE NULL\n END), 2) as margin_of_victory,\n -- Margin of Loss\n ROUND(AVG(CASE\n WHEN m.home = t.id AND m.home_cups < m.away_cups\n THEN m.away_cups - m.home_cups\n WHEN m.away = t.id AND m.away_cups < m.home_cups\n THEN m.home_cups - m.away_cups\n ELSE NULL\n END), 2) as margin_of_loss\n FROM players p, teams t, matches m, tournaments tour\n WHERE\n t.players LIKE '%\"' || p.id || '\"%' AND\n (m.home = t.id OR m.away = t.id) AND\n m.tournament = tour.id AND\n m.status = 'ended'\n GROUP BY p.id"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("number3837590211")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
28
pb_migrations/1758054877_updated_tournaments.js
Normal file
28
pb_migrations/1758054877_updated_tournaments.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_340646327")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(11, new Field({
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_3072146508",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation1584152981",
|
||||||
|
"maxSelect": 999,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "free_agents",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_340646327")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("relation1584152981")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
84
pb_migrations/1758081731_created_reactions.js
Normal file
84
pb_migrations/1758081731_created_reactions.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "[a-z0-9]{15}",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 15,
|
||||||
|
"min": 15,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3058431538",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "emoji",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_3072146508",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation2551806565",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "player",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "autodate2990389176",
|
||||||
|
"name": "created",
|
||||||
|
"onCreate": true,
|
||||||
|
"onUpdate": false,
|
||||||
|
"presentable": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "autodate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "autodate3332085495",
|
||||||
|
"name": "updated",
|
||||||
|
"onCreate": true,
|
||||||
|
"onUpdate": true,
|
||||||
|
"presentable": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "autodate"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_1549310251",
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "reactions",
|
||||||
|
"system": false,
|
||||||
|
"type": "base",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1549310251");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
})
|
||||||
28
pb_migrations/1758123221_updated_reactions.js
Normal file
28
pb_migrations/1758123221_updated_reactions.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1549310251")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(3, new Field({
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_2541054544",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation2052834565",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "match",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1549310251")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("relation2052834565")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
85
pb_migrations/1758379630_created_badges.js
Normal file
85
pb_migrations/1758379630_created_badges.js
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "[a-z0-9]{15}",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 15,
|
||||||
|
"min": 15,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text1579384326",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "name",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text1843675174",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "description",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "autodate2990389176",
|
||||||
|
"name": "created",
|
||||||
|
"onCreate": true,
|
||||||
|
"onUpdate": false,
|
||||||
|
"presentable": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "autodate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "autodate3332085495",
|
||||||
|
"name": "updated",
|
||||||
|
"onCreate": true,
|
||||||
|
"onUpdate": true,
|
||||||
|
"presentable": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "autodate"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_1340419796",
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "badges",
|
||||||
|
"system": false,
|
||||||
|
"type": "base",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1340419796");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
})
|
||||||
28
pb_migrations/1758380013_updated_players.js
Normal file
28
pb_migrations/1758380013_updated_players.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3072146508")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(5, new Field({
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_1340419796",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation2029409178",
|
||||||
|
"maxSelect": 999,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "badges",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3072146508")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("relation2029409178")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
28
pb_migrations/1758385120_updated_players.js
Normal file
28
pb_migrations/1758385120_updated_players.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3072146508")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(6, new Field({
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_1340419796",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation2813965191",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "featured_badge",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3072146508")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("relation2813965191")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
84
pb_migrations/1758388728_created_free_agents.js
Normal file
84
pb_migrations/1758388728_created_free_agents.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "[a-z0-9]{15}",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 15,
|
||||||
|
"min": 15,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_3072146508",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation2551806565",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "player",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text1146066909",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "phone",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "autodate2990389176",
|
||||||
|
"name": "created",
|
||||||
|
"onCreate": true,
|
||||||
|
"onUpdate": false,
|
||||||
|
"presentable": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "autodate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "autodate3332085495",
|
||||||
|
"name": "updated",
|
||||||
|
"onCreate": true,
|
||||||
|
"onUpdate": true,
|
||||||
|
"presentable": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "autodate"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_2929550049",
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "free_agents",
|
||||||
|
"system": false,
|
||||||
|
"type": "base",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2929550049");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
})
|
||||||
28
pb_migrations/1758402128_updated_free_agents.js
Normal file
28
pb_migrations/1758402128_updated_free_agents.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2929550049")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(3, new Field({
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_340646327",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation3177167065",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "tournament",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2929550049")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("relation3177167065")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
28
pb_migrations/1758402424_updated_tournaments.js
Normal file
28
pb_migrations/1758402424_updated_tournaments.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_340646327")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("relation1584152981")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_340646327")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(11, new Field({
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_3072146508",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation1584152981",
|
||||||
|
"maxSelect": 999,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "free_agents",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
28
pb_migrations/1758575563_updated_free_agents.js
Normal file
28
pb_migrations/1758575563_updated_free_agents.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2929550049")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"createRule": "",
|
||||||
|
"deleteRule": "",
|
||||||
|
"listRule": "",
|
||||||
|
"updateRule": "",
|
||||||
|
"viewRule": ""
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2929550049")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"listRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
28
pb_migrations/1758575597_updated_free_agents.js
Normal file
28
pb_migrations/1758575597_updated_free_agents.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2929550049")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"listRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_2929550049")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"createRule": "",
|
||||||
|
"deleteRule": "",
|
||||||
|
"listRule": "",
|
||||||
|
"updateRule": "",
|
||||||
|
"viewRule": ""
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
364
server.ts
Normal file
364
server.ts
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
/**
|
||||||
|
* TanStack Start Production Server with Bun
|
||||||
|
*
|
||||||
|
* A high-performance production server for TanStack Start applications that
|
||||||
|
* implements intelligent static asset loading with configurable memory management.
|
||||||
|
*
|
||||||
|
* Features:
|
||||||
|
* - Hybrid loading strategy (preload small files, serve large files on-demand)
|
||||||
|
* - Configurable file filtering with include/exclude patterns
|
||||||
|
* - Memory-efficient response generation
|
||||||
|
* - Production-ready caching headers
|
||||||
|
*
|
||||||
|
* Environment Variables:
|
||||||
|
*
|
||||||
|
* PORT (number)
|
||||||
|
* - Server port number
|
||||||
|
* - Default: 3000
|
||||||
|
*
|
||||||
|
* STATIC_PRELOAD_MAX_BYTES (number)
|
||||||
|
* - Maximum file size in bytes to preload into memory
|
||||||
|
* - Files larger than this will be served on-demand from disk
|
||||||
|
* - Default: 5242880 (5MB)
|
||||||
|
* - Example: STATIC_PRELOAD_MAX_BYTES=5242880 (5MB)
|
||||||
|
*
|
||||||
|
* STATIC_PRELOAD_INCLUDE (string)
|
||||||
|
* - Comma-separated list of glob patterns for files to include
|
||||||
|
* - If specified, only matching files are eligible for preloading
|
||||||
|
* - Patterns are matched against filenames only, not full paths
|
||||||
|
* - Example: STATIC_PRELOAD_INCLUDE="*.js,*.css,*.woff2"
|
||||||
|
*
|
||||||
|
* STATIC_PRELOAD_EXCLUDE (string)
|
||||||
|
* - Comma-separated list of glob patterns for files to exclude
|
||||||
|
* - Applied after include patterns
|
||||||
|
* - Patterns are matched against filenames only, not full paths
|
||||||
|
* - Example: STATIC_PRELOAD_EXCLUDE="*.map,*.txt"
|
||||||
|
*
|
||||||
|
* STATIC_PRELOAD_VERBOSE (boolean)
|
||||||
|
* - Enable detailed logging of loaded and skipped files
|
||||||
|
* - Default: false
|
||||||
|
* - Set to "true" to enable verbose output
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* bun run server.ts
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { readdir } from 'node:fs/promises'
|
||||||
|
import { join } from 'node:path'
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
const PORT = Number(process.env.PORT ?? 3000)
|
||||||
|
const CLIENT_DIR = './dist/client'
|
||||||
|
const SERVER_ENTRY = './dist/server/server.js'
|
||||||
|
|
||||||
|
// Preloading configuration from environment variables
|
||||||
|
const MAX_PRELOAD_BYTES = Number(
|
||||||
|
process.env.STATIC_PRELOAD_MAX_BYTES ?? 5 * 1024 * 1024, // 5MB default
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse comma-separated include patterns (no defaults)
|
||||||
|
const INCLUDE_PATTERNS = (process.env.STATIC_PRELOAD_INCLUDE ?? '')
|
||||||
|
.split(',')
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(globToRegExp)
|
||||||
|
|
||||||
|
// Parse comma-separated exclude patterns (no defaults)
|
||||||
|
const EXCLUDE_PATTERNS = (process.env.STATIC_PRELOAD_EXCLUDE ?? '')
|
||||||
|
.split(',')
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(globToRegExp)
|
||||||
|
|
||||||
|
// Verbose logging flag
|
||||||
|
const VERBOSE = process.env.STATIC_PRELOAD_VERBOSE === 'true'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a simple glob pattern to a regular expression
|
||||||
|
* Supports * wildcard for matching any characters
|
||||||
|
*/
|
||||||
|
function globToRegExp(glob: string): RegExp {
|
||||||
|
// Escape regex special chars except *, then replace * with .*
|
||||||
|
const escaped = glob
|
||||||
|
.replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&')
|
||||||
|
.replace(/\*/g, '.*')
|
||||||
|
return new RegExp(`^${escaped}$`, 'i')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata for preloaded static assets
|
||||||
|
*/
|
||||||
|
interface AssetMetadata {
|
||||||
|
route: string
|
||||||
|
size: number
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of static asset preloading process
|
||||||
|
*/
|
||||||
|
interface PreloadResult {
|
||||||
|
routes: Record<string, () => Response>
|
||||||
|
loaded: Array<AssetMetadata>
|
||||||
|
skipped: Array<AssetMetadata>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a file should be included based on configured patterns
|
||||||
|
*/
|
||||||
|
function shouldInclude(relativePath: string): boolean {
|
||||||
|
const fileName = relativePath.split(/[/\\]/).pop() ?? relativePath
|
||||||
|
|
||||||
|
// If include patterns are specified, file must match at least one
|
||||||
|
if (INCLUDE_PATTERNS.length > 0) {
|
||||||
|
if (!INCLUDE_PATTERNS.some((pattern) => pattern.test(fileName))) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If exclude patterns are specified, file must not match any
|
||||||
|
if (EXCLUDE_PATTERNS.some((pattern) => pattern.test(fileName))) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build static routes with intelligent preloading strategy
|
||||||
|
* Small files are loaded into memory, large files are served on-demand
|
||||||
|
*/
|
||||||
|
async function buildStaticRoutes(clientDir: string): Promise<PreloadResult> {
|
||||||
|
const routes: Record<string, () => Response> = {}
|
||||||
|
const loaded: Array<AssetMetadata> = []
|
||||||
|
const skipped: Array<AssetMetadata> = []
|
||||||
|
|
||||||
|
console.log(`📦 Loading static assets from ${clientDir}...`)
|
||||||
|
console.log(
|
||||||
|
` Max preload size: ${(MAX_PRELOAD_BYTES / 1024 / 1024).toFixed(2)} MB`,
|
||||||
|
)
|
||||||
|
if (INCLUDE_PATTERNS.length > 0) {
|
||||||
|
console.log(
|
||||||
|
` Include patterns: ${process.env.STATIC_PRELOAD_INCLUDE ?? ''}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (EXCLUDE_PATTERNS.length > 0) {
|
||||||
|
console.log(
|
||||||
|
` Exclude patterns: ${process.env.STATIC_PRELOAD_EXCLUDE ?? ''}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let totalPreloadedBytes = 0
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Read all files recursively
|
||||||
|
const files = await readdir(clientDir, { recursive: true })
|
||||||
|
|
||||||
|
for (const relativePath of files) {
|
||||||
|
const filepath = join(clientDir, relativePath)
|
||||||
|
const route = '/' + relativePath.replace(/\\/g, '/') // Handle Windows paths
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get file metadata
|
||||||
|
const file = Bun.file(filepath)
|
||||||
|
|
||||||
|
// Skip if file doesn't exist or is empty
|
||||||
|
if (!(await file.exists()) || file.size === 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata: AssetMetadata = {
|
||||||
|
route,
|
||||||
|
size: file.size,
|
||||||
|
type: file.type || 'application/octet-stream',
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if file should be preloaded
|
||||||
|
const matchesPattern = shouldInclude(relativePath)
|
||||||
|
const withinSizeLimit = file.size <= MAX_PRELOAD_BYTES
|
||||||
|
|
||||||
|
if (matchesPattern && withinSizeLimit) {
|
||||||
|
// Preload small files into memory
|
||||||
|
const bytes = await file.bytes()
|
||||||
|
|
||||||
|
routes[route] = () =>
|
||||||
|
new Response(bytes, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': metadata.type,
|
||||||
|
'Cache-Control': 'public, max-age=31536000, immutable',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
loaded.push({ ...metadata, size: bytes.byteLength })
|
||||||
|
totalPreloadedBytes += bytes.byteLength
|
||||||
|
} else {
|
||||||
|
// Serve large or filtered files on-demand
|
||||||
|
routes[route] = () => {
|
||||||
|
const fileOnDemand = Bun.file(filepath)
|
||||||
|
return new Response(fileOnDemand, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': metadata.type,
|
||||||
|
'Cache-Control': 'public, max-age=3600',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
skipped.push(metadata)
|
||||||
|
}
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (error instanceof Error && error.name !== 'EISDIR') {
|
||||||
|
console.error(`❌ Failed to load ${filepath}:`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always show file overview in Vite-like format first
|
||||||
|
if (loaded.length > 0 || skipped.length > 0) {
|
||||||
|
const allFiles = [...loaded, ...skipped].sort((a, b) =>
|
||||||
|
a.route.localeCompare(b.route),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Calculate max path length for alignment
|
||||||
|
const maxPathLength = Math.min(
|
||||||
|
Math.max(...allFiles.map((f) => f.route.length)),
|
||||||
|
60,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Format file size with KB and gzip estimation
|
||||||
|
const formatFileSize = (bytes: number) => {
|
||||||
|
const kb = bytes / 1024
|
||||||
|
// Rough gzip estimation (typically 30-70% compression)
|
||||||
|
const gzipKb = kb * 0.35
|
||||||
|
return {
|
||||||
|
size: kb < 100 ? kb.toFixed(2) : kb.toFixed(1),
|
||||||
|
gzip: gzipKb < 100 ? gzipKb.toFixed(2) : gzipKb.toFixed(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loaded.length > 0) {
|
||||||
|
console.log('\n📁 Preloaded into memory:')
|
||||||
|
loaded
|
||||||
|
.sort((a, b) => a.route.localeCompare(b.route))
|
||||||
|
.forEach((file) => {
|
||||||
|
const { size, gzip } = formatFileSize(file.size)
|
||||||
|
const paddedPath = file.route.padEnd(maxPathLength)
|
||||||
|
const sizeStr = `${size.padStart(7)} kB`
|
||||||
|
const gzipStr = `gzip: ${gzip.padStart(6)} kB`
|
||||||
|
console.log(` ${paddedPath} ${sizeStr} │ ${gzipStr}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipped.length > 0) {
|
||||||
|
console.log('\n💾 Served on-demand:')
|
||||||
|
skipped
|
||||||
|
.sort((a, b) => a.route.localeCompare(b.route))
|
||||||
|
.forEach((file) => {
|
||||||
|
const { size, gzip } = formatFileSize(file.size)
|
||||||
|
const paddedPath = file.route.padEnd(maxPathLength)
|
||||||
|
const sizeStr = `${size.padStart(7)} kB`
|
||||||
|
const gzipStr = `gzip: ${gzip.padStart(6)} kB`
|
||||||
|
console.log(` ${paddedPath} ${sizeStr} │ ${gzipStr}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show detailed verbose info if enabled
|
||||||
|
if (VERBOSE) {
|
||||||
|
console.log('\n📊 Detailed file information:')
|
||||||
|
allFiles.forEach((file) => {
|
||||||
|
const isPreloaded = loaded.includes(file)
|
||||||
|
const status = isPreloaded ? '[MEMORY]' : '[ON-DEMAND]'
|
||||||
|
const reason =
|
||||||
|
!isPreloaded && file.size > MAX_PRELOAD_BYTES
|
||||||
|
? ' (too large)'
|
||||||
|
: !isPreloaded
|
||||||
|
? ' (filtered)'
|
||||||
|
: ''
|
||||||
|
console.log(
|
||||||
|
` ${status.padEnd(12)} ${file.route} - ${file.type}${reason}`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log summary after the file list
|
||||||
|
console.log() // Empty line for separation
|
||||||
|
if (loaded.length > 0) {
|
||||||
|
console.log(
|
||||||
|
`✅ Preloaded ${String(loaded.length)} files (${(totalPreloadedBytes / 1024 / 1024).toFixed(2)} MB) into memory`,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
console.log('ℹ️ No files preloaded into memory')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipped.length > 0) {
|
||||||
|
const tooLarge = skipped.filter((f) => f.size > MAX_PRELOAD_BYTES).length
|
||||||
|
const filtered = skipped.length - tooLarge
|
||||||
|
console.log(
|
||||||
|
`ℹ️ ${String(skipped.length)} files will be served on-demand (${String(tooLarge)} too large, ${String(filtered)} filtered)`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ Failed to load static files from ${clientDir}:`, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { routes, loaded, skipped }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the production server
|
||||||
|
*/
|
||||||
|
async function startServer() {
|
||||||
|
console.log('🚀 Starting production server...')
|
||||||
|
|
||||||
|
// Load TanStack Start server handler
|
||||||
|
let handler: { fetch: (request: Request) => Response | Promise<Response> }
|
||||||
|
try {
|
||||||
|
const serverModule = (await import(SERVER_ENTRY)) as {
|
||||||
|
default: { fetch: (request: Request) => Response | Promise<Response> }
|
||||||
|
}
|
||||||
|
handler = serverModule.default
|
||||||
|
console.log('✅ TanStack Start handler loaded')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Failed to load server handler:', error)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build static routes with intelligent preloading
|
||||||
|
const { routes } = await buildStaticRoutes(CLIENT_DIR)
|
||||||
|
|
||||||
|
// Create Bun server
|
||||||
|
const server = Bun.serve({
|
||||||
|
port: PORT,
|
||||||
|
|
||||||
|
routes: {
|
||||||
|
// Serve static assets (preloaded or on-demand)
|
||||||
|
...routes,
|
||||||
|
|
||||||
|
// Fallback to TanStack Start handler for all other routes
|
||||||
|
'/*': (request) => {
|
||||||
|
try {
|
||||||
|
return handler.fetch(request)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Server handler error:', error)
|
||||||
|
return new Response('Internal Server Error', { status: 500 })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Global error handler
|
||||||
|
error(error) {
|
||||||
|
console.error('Uncaught server error:', error)
|
||||||
|
return new Response('Internal Server Error', { status: 500 })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`\n🚀 Server running at http://localhost:${String(server.port)}\n`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the server
|
||||||
|
startServer().catch((error: unknown) => {
|
||||||
|
console.error('Failed to start server:', error)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
@@ -8,31 +8,37 @@
|
|||||||
// You should NOT make any changes in this file as it will be overwritten.
|
// You should NOT make any changes in this file as it will be overwritten.
|
||||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||||
|
|
||||||
import { createServerRootRoute } from '@tanstack/react-start/server'
|
|
||||||
|
|
||||||
import { Route as rootRouteImport } from './routes/__root'
|
import { Route as rootRouteImport } from './routes/__root'
|
||||||
import { Route as RefreshSessionRouteImport } from './routes/refresh-session'
|
import { Route as RefreshSessionRouteImport } from './routes/refresh-session'
|
||||||
import { Route as LogoutRouteImport } from './routes/logout'
|
import { Route as LogoutRouteImport } from './routes/logout'
|
||||||
import { Route as LoginRouteImport } from './routes/login'
|
import { Route as LoginRouteImport } from './routes/login'
|
||||||
import { Route as AuthedRouteImport } from './routes/_authed'
|
import { Route as AuthedRouteImport } from './routes/_authed'
|
||||||
import { Route as AuthedIndexRouteImport } from './routes/_authed/index'
|
import { Route as AuthedIndexRouteImport } from './routes/_authed/index'
|
||||||
|
import { Route as AuthedStatsRouteImport } from './routes/_authed/stats'
|
||||||
import { Route as AuthedSettingsRouteImport } from './routes/_authed/settings'
|
import { Route as AuthedSettingsRouteImport } from './routes/_authed/settings'
|
||||||
import { Route as AuthedAdminRouteImport } from './routes/_authed/admin'
|
import { Route as AuthedAdminRouteImport } from './routes/_authed/admin'
|
||||||
import { Route as AuthedTournamentsIndexRouteImport } from './routes/_authed/tournaments/index'
|
import { Route as AuthedTournamentsIndexRouteImport } from './routes/_authed/tournaments/index'
|
||||||
import { Route as AuthedAdminIndexRouteImport } from './routes/_authed/admin/index'
|
import { Route as AuthedAdminIndexRouteImport } from './routes/_authed/admin/index'
|
||||||
|
import { Route as ApiTournamentsUploadLogoRouteImport } from './routes/api/tournaments/upload-logo'
|
||||||
|
import { Route as ApiTeamsUploadLogoRouteImport } from './routes/api/teams/upload-logo'
|
||||||
|
import { Route as ApiSpotifyTokenRouteImport } from './routes/api/spotify/token'
|
||||||
|
import { Route as ApiSpotifySearchRouteImport } from './routes/api/spotify/search'
|
||||||
|
import { Route as ApiSpotifyResumeRouteImport } from './routes/api/spotify/resume'
|
||||||
|
import { Route as ApiSpotifyPlaybackRouteImport } from './routes/api/spotify/playback'
|
||||||
|
import { Route as ApiSpotifyCaptureRouteImport } from './routes/api/spotify/capture'
|
||||||
|
import { Route as ApiSpotifyCallbackRouteImport } from './routes/api/spotify/callback'
|
||||||
|
import { Route as ApiEventsSplatRouteImport } from './routes/api/events.$'
|
||||||
|
import { Route as ApiAuthSplatRouteImport } from './routes/api/auth.$'
|
||||||
import { Route as AuthedTournamentsTournamentIdRouteImport } from './routes/_authed/tournaments/$tournamentId'
|
import { Route as AuthedTournamentsTournamentIdRouteImport } from './routes/_authed/tournaments/$tournamentId'
|
||||||
import { Route as AuthedTeamsTeamIdRouteImport } from './routes/_authed/teams.$teamId'
|
import { Route as AuthedTeamsTeamIdRouteImport } from './routes/_authed/teams.$teamId'
|
||||||
import { Route as AuthedProfilePlayerIdRouteImport } from './routes/_authed/profile.$playerId'
|
import { Route as AuthedProfilePlayerIdRouteImport } from './routes/_authed/profile.$playerId'
|
||||||
import { Route as AuthedAdminPreviewRouteImport } from './routes/_authed/admin/preview'
|
import { Route as AuthedAdminPreviewRouteImport } from './routes/_authed/admin/preview'
|
||||||
import { Route as AuthedAdminTournamentsIndexRouteImport } from './routes/_authed/admin/tournaments/index'
|
import { Route as AuthedAdminTournamentsIndexRouteImport } from './routes/_authed/admin/tournaments/index'
|
||||||
import { Route as AuthedAdminTournamentsIdRouteImport } from './routes/_authed/admin/tournaments/$id'
|
import { Route as AuthedTournamentsIdBracketRouteImport } from './routes/_authed/tournaments/$id.bracket'
|
||||||
|
import { Route as AuthedAdminTournamentsIdIndexRouteImport } from './routes/_authed/admin/tournaments/$id/index'
|
||||||
|
import { Route as ApiFilesCollectionRecordIdFileRouteImport } from './routes/api/files/$collection/$recordId/$file'
|
||||||
import { Route as AuthedAdminTournamentsRunIdRouteImport } from './routes/_authed/admin/tournaments/run.$id'
|
import { Route as AuthedAdminTournamentsRunIdRouteImport } from './routes/_authed/admin/tournaments/run.$id'
|
||||||
import { ServerRoute as ApiTournamentsUploadLogoServerRouteImport } from './routes/api/tournaments/upload-logo'
|
import { Route as AuthedAdminTournamentsIdTeamsRouteImport } from './routes/_authed/admin/tournaments/$id/teams'
|
||||||
import { ServerRoute as ApiEventsSplatServerRouteImport } from './routes/api/events.$'
|
|
||||||
import { ServerRoute as ApiAuthSplatServerRouteImport } from './routes/api/auth.$'
|
|
||||||
import { ServerRoute as ApiFilesCollectionRecordIdFileServerRouteImport } from './routes/api/files/$collection/$recordId/$file'
|
|
||||||
|
|
||||||
const rootServerRouteImport = createServerRootRoute()
|
|
||||||
|
|
||||||
const RefreshSessionRoute = RefreshSessionRouteImport.update({
|
const RefreshSessionRoute = RefreshSessionRouteImport.update({
|
||||||
id: '/refresh-session',
|
id: '/refresh-session',
|
||||||
@@ -58,6 +64,11 @@ const AuthedIndexRoute = AuthedIndexRouteImport.update({
|
|||||||
path: '/',
|
path: '/',
|
||||||
getParentRoute: () => AuthedRoute,
|
getParentRoute: () => AuthedRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
const AuthedStatsRoute = AuthedStatsRouteImport.update({
|
||||||
|
id: '/stats',
|
||||||
|
path: '/stats',
|
||||||
|
getParentRoute: () => AuthedRoute,
|
||||||
|
} as any)
|
||||||
const AuthedSettingsRoute = AuthedSettingsRouteImport.update({
|
const AuthedSettingsRoute = AuthedSettingsRouteImport.update({
|
||||||
id: '/settings',
|
id: '/settings',
|
||||||
path: '/settings',
|
path: '/settings',
|
||||||
@@ -78,6 +89,57 @@ const AuthedAdminIndexRoute = AuthedAdminIndexRouteImport.update({
|
|||||||
path: '/',
|
path: '/',
|
||||||
getParentRoute: () => AuthedAdminRoute,
|
getParentRoute: () => AuthedAdminRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
const ApiTournamentsUploadLogoRoute =
|
||||||
|
ApiTournamentsUploadLogoRouteImport.update({
|
||||||
|
id: '/api/tournaments/upload-logo',
|
||||||
|
path: '/api/tournaments/upload-logo',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
|
const ApiTeamsUploadLogoRoute = ApiTeamsUploadLogoRouteImport.update({
|
||||||
|
id: '/api/teams/upload-logo',
|
||||||
|
path: '/api/teams/upload-logo',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
|
const ApiSpotifyTokenRoute = ApiSpotifyTokenRouteImport.update({
|
||||||
|
id: '/api/spotify/token',
|
||||||
|
path: '/api/spotify/token',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
|
const ApiSpotifySearchRoute = ApiSpotifySearchRouteImport.update({
|
||||||
|
id: '/api/spotify/search',
|
||||||
|
path: '/api/spotify/search',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
|
const ApiSpotifyResumeRoute = ApiSpotifyResumeRouteImport.update({
|
||||||
|
id: '/api/spotify/resume',
|
||||||
|
path: '/api/spotify/resume',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
|
const ApiSpotifyPlaybackRoute = ApiSpotifyPlaybackRouteImport.update({
|
||||||
|
id: '/api/spotify/playback',
|
||||||
|
path: '/api/spotify/playback',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
|
const ApiSpotifyCaptureRoute = ApiSpotifyCaptureRouteImport.update({
|
||||||
|
id: '/api/spotify/capture',
|
||||||
|
path: '/api/spotify/capture',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
|
const ApiSpotifyCallbackRoute = ApiSpotifyCallbackRouteImport.update({
|
||||||
|
id: '/api/spotify/callback',
|
||||||
|
path: '/api/spotify/callback',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
|
const ApiEventsSplatRoute = ApiEventsSplatRouteImport.update({
|
||||||
|
id: '/api/events/$',
|
||||||
|
path: '/api/events/$',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
|
const ApiAuthSplatRoute = ApiAuthSplatRouteImport.update({
|
||||||
|
id: '/api/auth/$',
|
||||||
|
path: '/api/auth/$',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
const AuthedTournamentsTournamentIdRoute =
|
const AuthedTournamentsTournamentIdRoute =
|
||||||
AuthedTournamentsTournamentIdRouteImport.update({
|
AuthedTournamentsTournamentIdRouteImport.update({
|
||||||
id: '/tournaments/$tournamentId',
|
id: '/tournaments/$tournamentId',
|
||||||
@@ -105,39 +167,35 @@ const AuthedAdminTournamentsIndexRoute =
|
|||||||
path: '/tournaments/',
|
path: '/tournaments/',
|
||||||
getParentRoute: () => AuthedAdminRoute,
|
getParentRoute: () => AuthedAdminRoute,
|
||||||
} as any)
|
} as any)
|
||||||
const AuthedAdminTournamentsIdRoute =
|
const AuthedTournamentsIdBracketRoute =
|
||||||
AuthedAdminTournamentsIdRouteImport.update({
|
AuthedTournamentsIdBracketRouteImport.update({
|
||||||
id: '/tournaments/$id',
|
id: '/tournaments/$id/bracket',
|
||||||
path: '/tournaments/$id',
|
path: '/tournaments/$id/bracket',
|
||||||
|
getParentRoute: () => AuthedRoute,
|
||||||
|
} as any)
|
||||||
|
const AuthedAdminTournamentsIdIndexRoute =
|
||||||
|
AuthedAdminTournamentsIdIndexRouteImport.update({
|
||||||
|
id: '/tournaments/$id/',
|
||||||
|
path: '/tournaments/$id/',
|
||||||
getParentRoute: () => AuthedAdminRoute,
|
getParentRoute: () => AuthedAdminRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
const ApiFilesCollectionRecordIdFileRoute =
|
||||||
|
ApiFilesCollectionRecordIdFileRouteImport.update({
|
||||||
|
id: '/api/files/$collection/$recordId/$file',
|
||||||
|
path: '/api/files/$collection/$recordId/$file',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
const AuthedAdminTournamentsRunIdRoute =
|
const AuthedAdminTournamentsRunIdRoute =
|
||||||
AuthedAdminTournamentsRunIdRouteImport.update({
|
AuthedAdminTournamentsRunIdRouteImport.update({
|
||||||
id: '/tournaments/run/$id',
|
id: '/tournaments/run/$id',
|
||||||
path: '/tournaments/run/$id',
|
path: '/tournaments/run/$id',
|
||||||
getParentRoute: () => AuthedAdminRoute,
|
getParentRoute: () => AuthedAdminRoute,
|
||||||
} as any)
|
} as any)
|
||||||
const ApiTournamentsUploadLogoServerRoute =
|
const AuthedAdminTournamentsIdTeamsRoute =
|
||||||
ApiTournamentsUploadLogoServerRouteImport.update({
|
AuthedAdminTournamentsIdTeamsRouteImport.update({
|
||||||
id: '/api/tournaments/upload-logo',
|
id: '/tournaments/$id/teams',
|
||||||
path: '/api/tournaments/upload-logo',
|
path: '/tournaments/$id/teams',
|
||||||
getParentRoute: () => rootServerRouteImport,
|
getParentRoute: () => AuthedAdminRoute,
|
||||||
} as any)
|
|
||||||
const ApiEventsSplatServerRoute = ApiEventsSplatServerRouteImport.update({
|
|
||||||
id: '/api/events/$',
|
|
||||||
path: '/api/events/$',
|
|
||||||
getParentRoute: () => rootServerRouteImport,
|
|
||||||
} as any)
|
|
||||||
const ApiAuthSplatServerRoute = ApiAuthSplatServerRouteImport.update({
|
|
||||||
id: '/api/auth/$',
|
|
||||||
path: '/api/auth/$',
|
|
||||||
getParentRoute: () => rootServerRouteImport,
|
|
||||||
} as any)
|
|
||||||
const ApiFilesCollectionRecordIdFileServerRoute =
|
|
||||||
ApiFilesCollectionRecordIdFileServerRouteImport.update({
|
|
||||||
id: '/api/files/$collection/$recordId/$file',
|
|
||||||
path: '/api/files/$collection/$recordId/$file',
|
|
||||||
getParentRoute: () => rootServerRouteImport,
|
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
export interface FileRoutesByFullPath {
|
export interface FileRoutesByFullPath {
|
||||||
@@ -146,32 +204,60 @@ export interface FileRoutesByFullPath {
|
|||||||
'/refresh-session': typeof RefreshSessionRoute
|
'/refresh-session': typeof RefreshSessionRoute
|
||||||
'/admin': typeof AuthedAdminRouteWithChildren
|
'/admin': typeof AuthedAdminRouteWithChildren
|
||||||
'/settings': typeof AuthedSettingsRoute
|
'/settings': typeof AuthedSettingsRoute
|
||||||
|
'/stats': typeof AuthedStatsRoute
|
||||||
'/': typeof AuthedIndexRoute
|
'/': typeof AuthedIndexRoute
|
||||||
'/admin/preview': typeof AuthedAdminPreviewRoute
|
'/admin/preview': typeof AuthedAdminPreviewRoute
|
||||||
'/profile/$playerId': typeof AuthedProfilePlayerIdRoute
|
'/profile/$playerId': typeof AuthedProfilePlayerIdRoute
|
||||||
'/teams/$teamId': typeof AuthedTeamsTeamIdRoute
|
'/teams/$teamId': typeof AuthedTeamsTeamIdRoute
|
||||||
'/tournaments/$tournamentId': typeof AuthedTournamentsTournamentIdRoute
|
'/tournaments/$tournamentId': typeof AuthedTournamentsTournamentIdRoute
|
||||||
|
'/api/auth/$': typeof ApiAuthSplatRoute
|
||||||
|
'/api/events/$': typeof ApiEventsSplatRoute
|
||||||
|
'/api/spotify/callback': typeof ApiSpotifyCallbackRoute
|
||||||
|
'/api/spotify/capture': typeof ApiSpotifyCaptureRoute
|
||||||
|
'/api/spotify/playback': typeof ApiSpotifyPlaybackRoute
|
||||||
|
'/api/spotify/resume': typeof ApiSpotifyResumeRoute
|
||||||
|
'/api/spotify/search': typeof ApiSpotifySearchRoute
|
||||||
|
'/api/spotify/token': typeof ApiSpotifyTokenRoute
|
||||||
|
'/api/teams/upload-logo': typeof ApiTeamsUploadLogoRoute
|
||||||
|
'/api/tournaments/upload-logo': typeof ApiTournamentsUploadLogoRoute
|
||||||
'/admin/': typeof AuthedAdminIndexRoute
|
'/admin/': typeof AuthedAdminIndexRoute
|
||||||
'/tournaments': typeof AuthedTournamentsIndexRoute
|
'/tournaments': typeof AuthedTournamentsIndexRoute
|
||||||
'/admin/tournaments/$id': typeof AuthedAdminTournamentsIdRoute
|
'/tournaments/$id/bracket': typeof AuthedTournamentsIdBracketRoute
|
||||||
'/admin/tournaments': typeof AuthedAdminTournamentsIndexRoute
|
'/admin/tournaments': typeof AuthedAdminTournamentsIndexRoute
|
||||||
|
'/admin/tournaments/$id/teams': typeof AuthedAdminTournamentsIdTeamsRoute
|
||||||
'/admin/tournaments/run/$id': typeof AuthedAdminTournamentsRunIdRoute
|
'/admin/tournaments/run/$id': typeof AuthedAdminTournamentsRunIdRoute
|
||||||
|
'/api/files/$collection/$recordId/$file': typeof ApiFilesCollectionRecordIdFileRoute
|
||||||
|
'/admin/tournaments/$id': typeof AuthedAdminTournamentsIdIndexRoute
|
||||||
}
|
}
|
||||||
export interface FileRoutesByTo {
|
export interface FileRoutesByTo {
|
||||||
'/login': typeof LoginRoute
|
'/login': typeof LoginRoute
|
||||||
'/logout': typeof LogoutRoute
|
'/logout': typeof LogoutRoute
|
||||||
'/refresh-session': typeof RefreshSessionRoute
|
'/refresh-session': typeof RefreshSessionRoute
|
||||||
'/settings': typeof AuthedSettingsRoute
|
'/settings': typeof AuthedSettingsRoute
|
||||||
|
'/stats': typeof AuthedStatsRoute
|
||||||
'/': typeof AuthedIndexRoute
|
'/': typeof AuthedIndexRoute
|
||||||
'/admin/preview': typeof AuthedAdminPreviewRoute
|
'/admin/preview': typeof AuthedAdminPreviewRoute
|
||||||
'/profile/$playerId': typeof AuthedProfilePlayerIdRoute
|
'/profile/$playerId': typeof AuthedProfilePlayerIdRoute
|
||||||
'/teams/$teamId': typeof AuthedTeamsTeamIdRoute
|
'/teams/$teamId': typeof AuthedTeamsTeamIdRoute
|
||||||
'/tournaments/$tournamentId': typeof AuthedTournamentsTournamentIdRoute
|
'/tournaments/$tournamentId': typeof AuthedTournamentsTournamentIdRoute
|
||||||
|
'/api/auth/$': typeof ApiAuthSplatRoute
|
||||||
|
'/api/events/$': typeof ApiEventsSplatRoute
|
||||||
|
'/api/spotify/callback': typeof ApiSpotifyCallbackRoute
|
||||||
|
'/api/spotify/capture': typeof ApiSpotifyCaptureRoute
|
||||||
|
'/api/spotify/playback': typeof ApiSpotifyPlaybackRoute
|
||||||
|
'/api/spotify/resume': typeof ApiSpotifyResumeRoute
|
||||||
|
'/api/spotify/search': typeof ApiSpotifySearchRoute
|
||||||
|
'/api/spotify/token': typeof ApiSpotifyTokenRoute
|
||||||
|
'/api/teams/upload-logo': typeof ApiTeamsUploadLogoRoute
|
||||||
|
'/api/tournaments/upload-logo': typeof ApiTournamentsUploadLogoRoute
|
||||||
'/admin': typeof AuthedAdminIndexRoute
|
'/admin': typeof AuthedAdminIndexRoute
|
||||||
'/tournaments': typeof AuthedTournamentsIndexRoute
|
'/tournaments': typeof AuthedTournamentsIndexRoute
|
||||||
'/admin/tournaments/$id': typeof AuthedAdminTournamentsIdRoute
|
'/tournaments/$id/bracket': typeof AuthedTournamentsIdBracketRoute
|
||||||
'/admin/tournaments': typeof AuthedAdminTournamentsIndexRoute
|
'/admin/tournaments': typeof AuthedAdminTournamentsIndexRoute
|
||||||
|
'/admin/tournaments/$id/teams': typeof AuthedAdminTournamentsIdTeamsRoute
|
||||||
'/admin/tournaments/run/$id': typeof AuthedAdminTournamentsRunIdRoute
|
'/admin/tournaments/run/$id': typeof AuthedAdminTournamentsRunIdRoute
|
||||||
|
'/api/files/$collection/$recordId/$file': typeof ApiFilesCollectionRecordIdFileRoute
|
||||||
|
'/admin/tournaments/$id': typeof AuthedAdminTournamentsIdIndexRoute
|
||||||
}
|
}
|
||||||
export interface FileRoutesById {
|
export interface FileRoutesById {
|
||||||
__root__: typeof rootRouteImport
|
__root__: typeof rootRouteImport
|
||||||
@@ -181,16 +267,30 @@ export interface FileRoutesById {
|
|||||||
'/refresh-session': typeof RefreshSessionRoute
|
'/refresh-session': typeof RefreshSessionRoute
|
||||||
'/_authed/admin': typeof AuthedAdminRouteWithChildren
|
'/_authed/admin': typeof AuthedAdminRouteWithChildren
|
||||||
'/_authed/settings': typeof AuthedSettingsRoute
|
'/_authed/settings': typeof AuthedSettingsRoute
|
||||||
|
'/_authed/stats': typeof AuthedStatsRoute
|
||||||
'/_authed/': typeof AuthedIndexRoute
|
'/_authed/': typeof AuthedIndexRoute
|
||||||
'/_authed/admin/preview': typeof AuthedAdminPreviewRoute
|
'/_authed/admin/preview': typeof AuthedAdminPreviewRoute
|
||||||
'/_authed/profile/$playerId': typeof AuthedProfilePlayerIdRoute
|
'/_authed/profile/$playerId': typeof AuthedProfilePlayerIdRoute
|
||||||
'/_authed/teams/$teamId': typeof AuthedTeamsTeamIdRoute
|
'/_authed/teams/$teamId': typeof AuthedTeamsTeamIdRoute
|
||||||
'/_authed/tournaments/$tournamentId': typeof AuthedTournamentsTournamentIdRoute
|
'/_authed/tournaments/$tournamentId': typeof AuthedTournamentsTournamentIdRoute
|
||||||
|
'/api/auth/$': typeof ApiAuthSplatRoute
|
||||||
|
'/api/events/$': typeof ApiEventsSplatRoute
|
||||||
|
'/api/spotify/callback': typeof ApiSpotifyCallbackRoute
|
||||||
|
'/api/spotify/capture': typeof ApiSpotifyCaptureRoute
|
||||||
|
'/api/spotify/playback': typeof ApiSpotifyPlaybackRoute
|
||||||
|
'/api/spotify/resume': typeof ApiSpotifyResumeRoute
|
||||||
|
'/api/spotify/search': typeof ApiSpotifySearchRoute
|
||||||
|
'/api/spotify/token': typeof ApiSpotifyTokenRoute
|
||||||
|
'/api/teams/upload-logo': typeof ApiTeamsUploadLogoRoute
|
||||||
|
'/api/tournaments/upload-logo': typeof ApiTournamentsUploadLogoRoute
|
||||||
'/_authed/admin/': typeof AuthedAdminIndexRoute
|
'/_authed/admin/': typeof AuthedAdminIndexRoute
|
||||||
'/_authed/tournaments/': typeof AuthedTournamentsIndexRoute
|
'/_authed/tournaments/': typeof AuthedTournamentsIndexRoute
|
||||||
'/_authed/admin/tournaments/$id': typeof AuthedAdminTournamentsIdRoute
|
'/_authed/tournaments/$id/bracket': typeof AuthedTournamentsIdBracketRoute
|
||||||
'/_authed/admin/tournaments/': typeof AuthedAdminTournamentsIndexRoute
|
'/_authed/admin/tournaments/': typeof AuthedAdminTournamentsIndexRoute
|
||||||
|
'/_authed/admin/tournaments/$id/teams': typeof AuthedAdminTournamentsIdTeamsRoute
|
||||||
'/_authed/admin/tournaments/run/$id': typeof AuthedAdminTournamentsRunIdRoute
|
'/_authed/admin/tournaments/run/$id': typeof AuthedAdminTournamentsRunIdRoute
|
||||||
|
'/api/files/$collection/$recordId/$file': typeof ApiFilesCollectionRecordIdFileRoute
|
||||||
|
'/_authed/admin/tournaments/$id/': typeof AuthedAdminTournamentsIdIndexRoute
|
||||||
}
|
}
|
||||||
export interface FileRouteTypes {
|
export interface FileRouteTypes {
|
||||||
fileRoutesByFullPath: FileRoutesByFullPath
|
fileRoutesByFullPath: FileRoutesByFullPath
|
||||||
@@ -200,32 +300,60 @@ export interface FileRouteTypes {
|
|||||||
| '/refresh-session'
|
| '/refresh-session'
|
||||||
| '/admin'
|
| '/admin'
|
||||||
| '/settings'
|
| '/settings'
|
||||||
|
| '/stats'
|
||||||
| '/'
|
| '/'
|
||||||
| '/admin/preview'
|
| '/admin/preview'
|
||||||
| '/profile/$playerId'
|
| '/profile/$playerId'
|
||||||
| '/teams/$teamId'
|
| '/teams/$teamId'
|
||||||
| '/tournaments/$tournamentId'
|
| '/tournaments/$tournamentId'
|
||||||
|
| '/api/auth/$'
|
||||||
|
| '/api/events/$'
|
||||||
|
| '/api/spotify/callback'
|
||||||
|
| '/api/spotify/capture'
|
||||||
|
| '/api/spotify/playback'
|
||||||
|
| '/api/spotify/resume'
|
||||||
|
| '/api/spotify/search'
|
||||||
|
| '/api/spotify/token'
|
||||||
|
| '/api/teams/upload-logo'
|
||||||
|
| '/api/tournaments/upload-logo'
|
||||||
| '/admin/'
|
| '/admin/'
|
||||||
| '/tournaments'
|
| '/tournaments'
|
||||||
| '/admin/tournaments/$id'
|
| '/tournaments/$id/bracket'
|
||||||
| '/admin/tournaments'
|
| '/admin/tournaments'
|
||||||
|
| '/admin/tournaments/$id/teams'
|
||||||
| '/admin/tournaments/run/$id'
|
| '/admin/tournaments/run/$id'
|
||||||
|
| '/api/files/$collection/$recordId/$file'
|
||||||
|
| '/admin/tournaments/$id'
|
||||||
fileRoutesByTo: FileRoutesByTo
|
fileRoutesByTo: FileRoutesByTo
|
||||||
to:
|
to:
|
||||||
| '/login'
|
| '/login'
|
||||||
| '/logout'
|
| '/logout'
|
||||||
| '/refresh-session'
|
| '/refresh-session'
|
||||||
| '/settings'
|
| '/settings'
|
||||||
|
| '/stats'
|
||||||
| '/'
|
| '/'
|
||||||
| '/admin/preview'
|
| '/admin/preview'
|
||||||
| '/profile/$playerId'
|
| '/profile/$playerId'
|
||||||
| '/teams/$teamId'
|
| '/teams/$teamId'
|
||||||
| '/tournaments/$tournamentId'
|
| '/tournaments/$tournamentId'
|
||||||
|
| '/api/auth/$'
|
||||||
|
| '/api/events/$'
|
||||||
|
| '/api/spotify/callback'
|
||||||
|
| '/api/spotify/capture'
|
||||||
|
| '/api/spotify/playback'
|
||||||
|
| '/api/spotify/resume'
|
||||||
|
| '/api/spotify/search'
|
||||||
|
| '/api/spotify/token'
|
||||||
|
| '/api/teams/upload-logo'
|
||||||
|
| '/api/tournaments/upload-logo'
|
||||||
| '/admin'
|
| '/admin'
|
||||||
| '/tournaments'
|
| '/tournaments'
|
||||||
| '/admin/tournaments/$id'
|
| '/tournaments/$id/bracket'
|
||||||
| '/admin/tournaments'
|
| '/admin/tournaments'
|
||||||
|
| '/admin/tournaments/$id/teams'
|
||||||
| '/admin/tournaments/run/$id'
|
| '/admin/tournaments/run/$id'
|
||||||
|
| '/api/files/$collection/$recordId/$file'
|
||||||
|
| '/admin/tournaments/$id'
|
||||||
id:
|
id:
|
||||||
| '__root__'
|
| '__root__'
|
||||||
| '/_authed'
|
| '/_authed'
|
||||||
@@ -234,16 +362,30 @@ export interface FileRouteTypes {
|
|||||||
| '/refresh-session'
|
| '/refresh-session'
|
||||||
| '/_authed/admin'
|
| '/_authed/admin'
|
||||||
| '/_authed/settings'
|
| '/_authed/settings'
|
||||||
|
| '/_authed/stats'
|
||||||
| '/_authed/'
|
| '/_authed/'
|
||||||
| '/_authed/admin/preview'
|
| '/_authed/admin/preview'
|
||||||
| '/_authed/profile/$playerId'
|
| '/_authed/profile/$playerId'
|
||||||
| '/_authed/teams/$teamId'
|
| '/_authed/teams/$teamId'
|
||||||
| '/_authed/tournaments/$tournamentId'
|
| '/_authed/tournaments/$tournamentId'
|
||||||
|
| '/api/auth/$'
|
||||||
|
| '/api/events/$'
|
||||||
|
| '/api/spotify/callback'
|
||||||
|
| '/api/spotify/capture'
|
||||||
|
| '/api/spotify/playback'
|
||||||
|
| '/api/spotify/resume'
|
||||||
|
| '/api/spotify/search'
|
||||||
|
| '/api/spotify/token'
|
||||||
|
| '/api/teams/upload-logo'
|
||||||
|
| '/api/tournaments/upload-logo'
|
||||||
| '/_authed/admin/'
|
| '/_authed/admin/'
|
||||||
| '/_authed/tournaments/'
|
| '/_authed/tournaments/'
|
||||||
| '/_authed/admin/tournaments/$id'
|
| '/_authed/tournaments/$id/bracket'
|
||||||
| '/_authed/admin/tournaments/'
|
| '/_authed/admin/tournaments/'
|
||||||
|
| '/_authed/admin/tournaments/$id/teams'
|
||||||
| '/_authed/admin/tournaments/run/$id'
|
| '/_authed/admin/tournaments/run/$id'
|
||||||
|
| '/api/files/$collection/$recordId/$file'
|
||||||
|
| '/_authed/admin/tournaments/$id/'
|
||||||
fileRoutesById: FileRoutesById
|
fileRoutesById: FileRoutesById
|
||||||
}
|
}
|
||||||
export interface RootRouteChildren {
|
export interface RootRouteChildren {
|
||||||
@@ -251,52 +393,17 @@ export interface RootRouteChildren {
|
|||||||
LoginRoute: typeof LoginRoute
|
LoginRoute: typeof LoginRoute
|
||||||
LogoutRoute: typeof LogoutRoute
|
LogoutRoute: typeof LogoutRoute
|
||||||
RefreshSessionRoute: typeof RefreshSessionRoute
|
RefreshSessionRoute: typeof RefreshSessionRoute
|
||||||
}
|
ApiAuthSplatRoute: typeof ApiAuthSplatRoute
|
||||||
export interface FileServerRoutesByFullPath {
|
ApiEventsSplatRoute: typeof ApiEventsSplatRoute
|
||||||
'/api/auth/$': typeof ApiAuthSplatServerRoute
|
ApiSpotifyCallbackRoute: typeof ApiSpotifyCallbackRoute
|
||||||
'/api/events/$': typeof ApiEventsSplatServerRoute
|
ApiSpotifyCaptureRoute: typeof ApiSpotifyCaptureRoute
|
||||||
'/api/tournaments/upload-logo': typeof ApiTournamentsUploadLogoServerRoute
|
ApiSpotifyPlaybackRoute: typeof ApiSpotifyPlaybackRoute
|
||||||
'/api/files/$collection/$recordId/$file': typeof ApiFilesCollectionRecordIdFileServerRoute
|
ApiSpotifyResumeRoute: typeof ApiSpotifyResumeRoute
|
||||||
}
|
ApiSpotifySearchRoute: typeof ApiSpotifySearchRoute
|
||||||
export interface FileServerRoutesByTo {
|
ApiSpotifyTokenRoute: typeof ApiSpotifyTokenRoute
|
||||||
'/api/auth/$': typeof ApiAuthSplatServerRoute
|
ApiTeamsUploadLogoRoute: typeof ApiTeamsUploadLogoRoute
|
||||||
'/api/events/$': typeof ApiEventsSplatServerRoute
|
ApiTournamentsUploadLogoRoute: typeof ApiTournamentsUploadLogoRoute
|
||||||
'/api/tournaments/upload-logo': typeof ApiTournamentsUploadLogoServerRoute
|
ApiFilesCollectionRecordIdFileRoute: typeof ApiFilesCollectionRecordIdFileRoute
|
||||||
'/api/files/$collection/$recordId/$file': typeof ApiFilesCollectionRecordIdFileServerRoute
|
|
||||||
}
|
|
||||||
export interface FileServerRoutesById {
|
|
||||||
__root__: typeof rootServerRouteImport
|
|
||||||
'/api/auth/$': typeof ApiAuthSplatServerRoute
|
|
||||||
'/api/events/$': typeof ApiEventsSplatServerRoute
|
|
||||||
'/api/tournaments/upload-logo': typeof ApiTournamentsUploadLogoServerRoute
|
|
||||||
'/api/files/$collection/$recordId/$file': typeof ApiFilesCollectionRecordIdFileServerRoute
|
|
||||||
}
|
|
||||||
export interface FileServerRouteTypes {
|
|
||||||
fileServerRoutesByFullPath: FileServerRoutesByFullPath
|
|
||||||
fullPaths:
|
|
||||||
| '/api/auth/$'
|
|
||||||
| '/api/events/$'
|
|
||||||
| '/api/tournaments/upload-logo'
|
|
||||||
| '/api/files/$collection/$recordId/$file'
|
|
||||||
fileServerRoutesByTo: FileServerRoutesByTo
|
|
||||||
to:
|
|
||||||
| '/api/auth/$'
|
|
||||||
| '/api/events/$'
|
|
||||||
| '/api/tournaments/upload-logo'
|
|
||||||
| '/api/files/$collection/$recordId/$file'
|
|
||||||
id:
|
|
||||||
| '__root__'
|
|
||||||
| '/api/auth/$'
|
|
||||||
| '/api/events/$'
|
|
||||||
| '/api/tournaments/upload-logo'
|
|
||||||
| '/api/files/$collection/$recordId/$file'
|
|
||||||
fileServerRoutesById: FileServerRoutesById
|
|
||||||
}
|
|
||||||
export interface RootServerRouteChildren {
|
|
||||||
ApiAuthSplatServerRoute: typeof ApiAuthSplatServerRoute
|
|
||||||
ApiEventsSplatServerRoute: typeof ApiEventsSplatServerRoute
|
|
||||||
ApiTournamentsUploadLogoServerRoute: typeof ApiTournamentsUploadLogoServerRoute
|
|
||||||
ApiFilesCollectionRecordIdFileServerRoute: typeof ApiFilesCollectionRecordIdFileServerRoute
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@tanstack/react-router' {
|
declare module '@tanstack/react-router' {
|
||||||
@@ -336,6 +443,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof AuthedIndexRouteImport
|
preLoaderRoute: typeof AuthedIndexRouteImport
|
||||||
parentRoute: typeof AuthedRoute
|
parentRoute: typeof AuthedRoute
|
||||||
}
|
}
|
||||||
|
'/_authed/stats': {
|
||||||
|
id: '/_authed/stats'
|
||||||
|
path: '/stats'
|
||||||
|
fullPath: '/stats'
|
||||||
|
preLoaderRoute: typeof AuthedStatsRouteImport
|
||||||
|
parentRoute: typeof AuthedRoute
|
||||||
|
}
|
||||||
'/_authed/settings': {
|
'/_authed/settings': {
|
||||||
id: '/_authed/settings'
|
id: '/_authed/settings'
|
||||||
path: '/settings'
|
path: '/settings'
|
||||||
@@ -364,6 +478,76 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof AuthedAdminIndexRouteImport
|
preLoaderRoute: typeof AuthedAdminIndexRouteImport
|
||||||
parentRoute: typeof AuthedAdminRoute
|
parentRoute: typeof AuthedAdminRoute
|
||||||
}
|
}
|
||||||
|
'/api/tournaments/upload-logo': {
|
||||||
|
id: '/api/tournaments/upload-logo'
|
||||||
|
path: '/api/tournaments/upload-logo'
|
||||||
|
fullPath: '/api/tournaments/upload-logo'
|
||||||
|
preLoaderRoute: typeof ApiTournamentsUploadLogoRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
|
'/api/teams/upload-logo': {
|
||||||
|
id: '/api/teams/upload-logo'
|
||||||
|
path: '/api/teams/upload-logo'
|
||||||
|
fullPath: '/api/teams/upload-logo'
|
||||||
|
preLoaderRoute: typeof ApiTeamsUploadLogoRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
|
'/api/spotify/token': {
|
||||||
|
id: '/api/spotify/token'
|
||||||
|
path: '/api/spotify/token'
|
||||||
|
fullPath: '/api/spotify/token'
|
||||||
|
preLoaderRoute: typeof ApiSpotifyTokenRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
|
'/api/spotify/search': {
|
||||||
|
id: '/api/spotify/search'
|
||||||
|
path: '/api/spotify/search'
|
||||||
|
fullPath: '/api/spotify/search'
|
||||||
|
preLoaderRoute: typeof ApiSpotifySearchRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
|
'/api/spotify/resume': {
|
||||||
|
id: '/api/spotify/resume'
|
||||||
|
path: '/api/spotify/resume'
|
||||||
|
fullPath: '/api/spotify/resume'
|
||||||
|
preLoaderRoute: typeof ApiSpotifyResumeRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
|
'/api/spotify/playback': {
|
||||||
|
id: '/api/spotify/playback'
|
||||||
|
path: '/api/spotify/playback'
|
||||||
|
fullPath: '/api/spotify/playback'
|
||||||
|
preLoaderRoute: typeof ApiSpotifyPlaybackRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
|
'/api/spotify/capture': {
|
||||||
|
id: '/api/spotify/capture'
|
||||||
|
path: '/api/spotify/capture'
|
||||||
|
fullPath: '/api/spotify/capture'
|
||||||
|
preLoaderRoute: typeof ApiSpotifyCaptureRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
|
'/api/spotify/callback': {
|
||||||
|
id: '/api/spotify/callback'
|
||||||
|
path: '/api/spotify/callback'
|
||||||
|
fullPath: '/api/spotify/callback'
|
||||||
|
preLoaderRoute: typeof ApiSpotifyCallbackRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
|
'/api/events/$': {
|
||||||
|
id: '/api/events/$'
|
||||||
|
path: '/api/events/$'
|
||||||
|
fullPath: '/api/events/$'
|
||||||
|
preLoaderRoute: typeof ApiEventsSplatRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
|
'/api/auth/$': {
|
||||||
|
id: '/api/auth/$'
|
||||||
|
path: '/api/auth/$'
|
||||||
|
fullPath: '/api/auth/$'
|
||||||
|
preLoaderRoute: typeof ApiAuthSplatRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
'/_authed/tournaments/$tournamentId': {
|
'/_authed/tournaments/$tournamentId': {
|
||||||
id: '/_authed/tournaments/$tournamentId'
|
id: '/_authed/tournaments/$tournamentId'
|
||||||
path: '/tournaments/$tournamentId'
|
path: '/tournaments/$tournamentId'
|
||||||
@@ -399,13 +583,27 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof AuthedAdminTournamentsIndexRouteImport
|
preLoaderRoute: typeof AuthedAdminTournamentsIndexRouteImport
|
||||||
parentRoute: typeof AuthedAdminRoute
|
parentRoute: typeof AuthedAdminRoute
|
||||||
}
|
}
|
||||||
'/_authed/admin/tournaments/$id': {
|
'/_authed/tournaments/$id/bracket': {
|
||||||
id: '/_authed/admin/tournaments/$id'
|
id: '/_authed/tournaments/$id/bracket'
|
||||||
|
path: '/tournaments/$id/bracket'
|
||||||
|
fullPath: '/tournaments/$id/bracket'
|
||||||
|
preLoaderRoute: typeof AuthedTournamentsIdBracketRouteImport
|
||||||
|
parentRoute: typeof AuthedRoute
|
||||||
|
}
|
||||||
|
'/_authed/admin/tournaments/$id/': {
|
||||||
|
id: '/_authed/admin/tournaments/$id/'
|
||||||
path: '/tournaments/$id'
|
path: '/tournaments/$id'
|
||||||
fullPath: '/admin/tournaments/$id'
|
fullPath: '/admin/tournaments/$id'
|
||||||
preLoaderRoute: typeof AuthedAdminTournamentsIdRouteImport
|
preLoaderRoute: typeof AuthedAdminTournamentsIdIndexRouteImport
|
||||||
parentRoute: typeof AuthedAdminRoute
|
parentRoute: typeof AuthedAdminRoute
|
||||||
}
|
}
|
||||||
|
'/api/files/$collection/$recordId/$file': {
|
||||||
|
id: '/api/files/$collection/$recordId/$file'
|
||||||
|
path: '/api/files/$collection/$recordId/$file'
|
||||||
|
fullPath: '/api/files/$collection/$recordId/$file'
|
||||||
|
preLoaderRoute: typeof ApiFilesCollectionRecordIdFileRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
'/_authed/admin/tournaments/run/$id': {
|
'/_authed/admin/tournaments/run/$id': {
|
||||||
id: '/_authed/admin/tournaments/run/$id'
|
id: '/_authed/admin/tournaments/run/$id'
|
||||||
path: '/tournaments/run/$id'
|
path: '/tournaments/run/$id'
|
||||||
@@ -413,37 +611,12 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof AuthedAdminTournamentsRunIdRouteImport
|
preLoaderRoute: typeof AuthedAdminTournamentsRunIdRouteImport
|
||||||
parentRoute: typeof AuthedAdminRoute
|
parentRoute: typeof AuthedAdminRoute
|
||||||
}
|
}
|
||||||
}
|
'/_authed/admin/tournaments/$id/teams': {
|
||||||
}
|
id: '/_authed/admin/tournaments/$id/teams'
|
||||||
declare module '@tanstack/react-start/server' {
|
path: '/tournaments/$id/teams'
|
||||||
interface ServerFileRoutesByPath {
|
fullPath: '/admin/tournaments/$id/teams'
|
||||||
'/api/tournaments/upload-logo': {
|
preLoaderRoute: typeof AuthedAdminTournamentsIdTeamsRouteImport
|
||||||
id: '/api/tournaments/upload-logo'
|
parentRoute: typeof AuthedAdminRoute
|
||||||
path: '/api/tournaments/upload-logo'
|
|
||||||
fullPath: '/api/tournaments/upload-logo'
|
|
||||||
preLoaderRoute: typeof ApiTournamentsUploadLogoServerRouteImport
|
|
||||||
parentRoute: typeof rootServerRouteImport
|
|
||||||
}
|
|
||||||
'/api/events/$': {
|
|
||||||
id: '/api/events/$'
|
|
||||||
path: '/api/events/$'
|
|
||||||
fullPath: '/api/events/$'
|
|
||||||
preLoaderRoute: typeof ApiEventsSplatServerRouteImport
|
|
||||||
parentRoute: typeof rootServerRouteImport
|
|
||||||
}
|
|
||||||
'/api/auth/$': {
|
|
||||||
id: '/api/auth/$'
|
|
||||||
path: '/api/auth/$'
|
|
||||||
fullPath: '/api/auth/$'
|
|
||||||
preLoaderRoute: typeof ApiAuthSplatServerRouteImport
|
|
||||||
parentRoute: typeof rootServerRouteImport
|
|
||||||
}
|
|
||||||
'/api/files/$collection/$recordId/$file': {
|
|
||||||
id: '/api/files/$collection/$recordId/$file'
|
|
||||||
path: '/api/files/$collection/$recordId/$file'
|
|
||||||
fullPath: '/api/files/$collection/$recordId/$file'
|
|
||||||
preLoaderRoute: typeof ApiFilesCollectionRecordIdFileServerRouteImport
|
|
||||||
parentRoute: typeof rootServerRouteImport
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -451,17 +624,19 @@ declare module '@tanstack/react-start/server' {
|
|||||||
interface AuthedAdminRouteChildren {
|
interface AuthedAdminRouteChildren {
|
||||||
AuthedAdminPreviewRoute: typeof AuthedAdminPreviewRoute
|
AuthedAdminPreviewRoute: typeof AuthedAdminPreviewRoute
|
||||||
AuthedAdminIndexRoute: typeof AuthedAdminIndexRoute
|
AuthedAdminIndexRoute: typeof AuthedAdminIndexRoute
|
||||||
AuthedAdminTournamentsIdRoute: typeof AuthedAdminTournamentsIdRoute
|
|
||||||
AuthedAdminTournamentsIndexRoute: typeof AuthedAdminTournamentsIndexRoute
|
AuthedAdminTournamentsIndexRoute: typeof AuthedAdminTournamentsIndexRoute
|
||||||
|
AuthedAdminTournamentsIdTeamsRoute: typeof AuthedAdminTournamentsIdTeamsRoute
|
||||||
AuthedAdminTournamentsRunIdRoute: typeof AuthedAdminTournamentsRunIdRoute
|
AuthedAdminTournamentsRunIdRoute: typeof AuthedAdminTournamentsRunIdRoute
|
||||||
|
AuthedAdminTournamentsIdIndexRoute: typeof AuthedAdminTournamentsIdIndexRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthedAdminRouteChildren: AuthedAdminRouteChildren = {
|
const AuthedAdminRouteChildren: AuthedAdminRouteChildren = {
|
||||||
AuthedAdminPreviewRoute: AuthedAdminPreviewRoute,
|
AuthedAdminPreviewRoute: AuthedAdminPreviewRoute,
|
||||||
AuthedAdminIndexRoute: AuthedAdminIndexRoute,
|
AuthedAdminIndexRoute: AuthedAdminIndexRoute,
|
||||||
AuthedAdminTournamentsIdRoute: AuthedAdminTournamentsIdRoute,
|
|
||||||
AuthedAdminTournamentsIndexRoute: AuthedAdminTournamentsIndexRoute,
|
AuthedAdminTournamentsIndexRoute: AuthedAdminTournamentsIndexRoute,
|
||||||
|
AuthedAdminTournamentsIdTeamsRoute: AuthedAdminTournamentsIdTeamsRoute,
|
||||||
AuthedAdminTournamentsRunIdRoute: AuthedAdminTournamentsRunIdRoute,
|
AuthedAdminTournamentsRunIdRoute: AuthedAdminTournamentsRunIdRoute,
|
||||||
|
AuthedAdminTournamentsIdIndexRoute: AuthedAdminTournamentsIdIndexRoute,
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthedAdminRouteWithChildren = AuthedAdminRoute._addFileChildren(
|
const AuthedAdminRouteWithChildren = AuthedAdminRoute._addFileChildren(
|
||||||
@@ -471,21 +646,25 @@ const AuthedAdminRouteWithChildren = AuthedAdminRoute._addFileChildren(
|
|||||||
interface AuthedRouteChildren {
|
interface AuthedRouteChildren {
|
||||||
AuthedAdminRoute: typeof AuthedAdminRouteWithChildren
|
AuthedAdminRoute: typeof AuthedAdminRouteWithChildren
|
||||||
AuthedSettingsRoute: typeof AuthedSettingsRoute
|
AuthedSettingsRoute: typeof AuthedSettingsRoute
|
||||||
|
AuthedStatsRoute: typeof AuthedStatsRoute
|
||||||
AuthedIndexRoute: typeof AuthedIndexRoute
|
AuthedIndexRoute: typeof AuthedIndexRoute
|
||||||
AuthedProfilePlayerIdRoute: typeof AuthedProfilePlayerIdRoute
|
AuthedProfilePlayerIdRoute: typeof AuthedProfilePlayerIdRoute
|
||||||
AuthedTeamsTeamIdRoute: typeof AuthedTeamsTeamIdRoute
|
AuthedTeamsTeamIdRoute: typeof AuthedTeamsTeamIdRoute
|
||||||
AuthedTournamentsTournamentIdRoute: typeof AuthedTournamentsTournamentIdRoute
|
AuthedTournamentsTournamentIdRoute: typeof AuthedTournamentsTournamentIdRoute
|
||||||
AuthedTournamentsIndexRoute: typeof AuthedTournamentsIndexRoute
|
AuthedTournamentsIndexRoute: typeof AuthedTournamentsIndexRoute
|
||||||
|
AuthedTournamentsIdBracketRoute: typeof AuthedTournamentsIdBracketRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthedRouteChildren: AuthedRouteChildren = {
|
const AuthedRouteChildren: AuthedRouteChildren = {
|
||||||
AuthedAdminRoute: AuthedAdminRouteWithChildren,
|
AuthedAdminRoute: AuthedAdminRouteWithChildren,
|
||||||
AuthedSettingsRoute: AuthedSettingsRoute,
|
AuthedSettingsRoute: AuthedSettingsRoute,
|
||||||
|
AuthedStatsRoute: AuthedStatsRoute,
|
||||||
AuthedIndexRoute: AuthedIndexRoute,
|
AuthedIndexRoute: AuthedIndexRoute,
|
||||||
AuthedProfilePlayerIdRoute: AuthedProfilePlayerIdRoute,
|
AuthedProfilePlayerIdRoute: AuthedProfilePlayerIdRoute,
|
||||||
AuthedTeamsTeamIdRoute: AuthedTeamsTeamIdRoute,
|
AuthedTeamsTeamIdRoute: AuthedTeamsTeamIdRoute,
|
||||||
AuthedTournamentsTournamentIdRoute: AuthedTournamentsTournamentIdRoute,
|
AuthedTournamentsTournamentIdRoute: AuthedTournamentsTournamentIdRoute,
|
||||||
AuthedTournamentsIndexRoute: AuthedTournamentsIndexRoute,
|
AuthedTournamentsIndexRoute: AuthedTournamentsIndexRoute,
|
||||||
|
AuthedTournamentsIdBracketRoute: AuthedTournamentsIdBracketRoute,
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthedRouteWithChildren =
|
const AuthedRouteWithChildren =
|
||||||
@@ -496,17 +675,27 @@ const rootRouteChildren: RootRouteChildren = {
|
|||||||
LoginRoute: LoginRoute,
|
LoginRoute: LoginRoute,
|
||||||
LogoutRoute: LogoutRoute,
|
LogoutRoute: LogoutRoute,
|
||||||
RefreshSessionRoute: RefreshSessionRoute,
|
RefreshSessionRoute: RefreshSessionRoute,
|
||||||
|
ApiAuthSplatRoute: ApiAuthSplatRoute,
|
||||||
|
ApiEventsSplatRoute: ApiEventsSplatRoute,
|
||||||
|
ApiSpotifyCallbackRoute: ApiSpotifyCallbackRoute,
|
||||||
|
ApiSpotifyCaptureRoute: ApiSpotifyCaptureRoute,
|
||||||
|
ApiSpotifyPlaybackRoute: ApiSpotifyPlaybackRoute,
|
||||||
|
ApiSpotifyResumeRoute: ApiSpotifyResumeRoute,
|
||||||
|
ApiSpotifySearchRoute: ApiSpotifySearchRoute,
|
||||||
|
ApiSpotifyTokenRoute: ApiSpotifyTokenRoute,
|
||||||
|
ApiTeamsUploadLogoRoute: ApiTeamsUploadLogoRoute,
|
||||||
|
ApiTournamentsUploadLogoRoute: ApiTournamentsUploadLogoRoute,
|
||||||
|
ApiFilesCollectionRecordIdFileRoute: ApiFilesCollectionRecordIdFileRoute,
|
||||||
}
|
}
|
||||||
export const routeTree = rootRouteImport
|
export const routeTree = rootRouteImport
|
||||||
._addFileChildren(rootRouteChildren)
|
._addFileChildren(rootRouteChildren)
|
||||||
._addFileTypes<FileRouteTypes>()
|
._addFileTypes<FileRouteTypes>()
|
||||||
const rootServerRouteChildren: RootServerRouteChildren = {
|
|
||||||
ApiAuthSplatServerRoute: ApiAuthSplatServerRoute,
|
import type { getRouter } from './router.tsx'
|
||||||
ApiEventsSplatServerRoute: ApiEventsSplatServerRoute,
|
import type { createStart } from '@tanstack/react-start'
|
||||||
ApiTournamentsUploadLogoServerRoute: ApiTournamentsUploadLogoServerRoute,
|
declare module '@tanstack/react-start' {
|
||||||
ApiFilesCollectionRecordIdFileServerRoute:
|
interface Register {
|
||||||
ApiFilesCollectionRecordIdFileServerRoute,
|
ssr: true
|
||||||
|
router: Awaited<ReturnType<typeof getRouter>>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export const serverRouteTree = rootServerRouteImport
|
|
||||||
._addFileChildren(rootServerRouteChildren)
|
|
||||||
._addFileTypes<FileServerRouteTypes>()
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { QueryClient } from "@tanstack/react-query";
|
import { QueryClient } from "@tanstack/react-query";
|
||||||
import { createRouter as createTanStackRouter } from "@tanstack/react-router";
|
import { createRouter as createTanStackRouter } from "@tanstack/react-router";
|
||||||
import { routerWithQueryClient } from "@tanstack/react-router-with-query";
|
import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query";
|
||||||
import { routeTree } from "./routeTree.gen";
|
import { routeTree } from "./routeTree.gen";
|
||||||
import { DefaultCatchBoundary } from "../components/DefaultCatchBoundary";
|
import { DefaultCatchBoundary } from "../components/DefaultCatchBoundary";
|
||||||
import { defaultHeaderConfig } from "@/features/core/hooks/use-router-config";
|
import { defaultHeaderConfig } from "@/features/core/hooks/use-router-config";
|
||||||
|
|
||||||
export function createRouter() {
|
export function getRouter() {
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
queries: {
|
queries: {
|
||||||
@@ -18,8 +18,7 @@ export function createRouter() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return routerWithQueryClient(
|
const router = createTanStackRouter({
|
||||||
createTanStackRouter({
|
|
||||||
routeTree,
|
routeTree,
|
||||||
context: {
|
context: {
|
||||||
queryClient,
|
queryClient,
|
||||||
@@ -32,14 +31,19 @@ export function createRouter() {
|
|||||||
defaultPreload: "intent",
|
defaultPreload: "intent",
|
||||||
defaultErrorComponent: DefaultCatchBoundary,
|
defaultErrorComponent: DefaultCatchBoundary,
|
||||||
scrollRestoration: true,
|
scrollRestoration: true,
|
||||||
defaultViewTransition: true,
|
defaultViewTransition: false,
|
||||||
}),
|
});
|
||||||
|
|
||||||
|
setupRouterSsrQueryIntegration({
|
||||||
|
router,
|
||||||
queryClient
|
queryClient
|
||||||
);
|
})
|
||||||
|
|
||||||
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module "@tanstack/react-router" {
|
declare module "@tanstack/react-router" {
|
||||||
interface Register {
|
interface Register {
|
||||||
router: ReturnType<typeof createRouter>;
|
router: ReturnType<typeof getRouter>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import "@mantine/core/styles.css";
|
|
||||||
import "@mantine/dates/styles.css";
|
|
||||||
import "@mantine/carousel/styles.css";
|
|
||||||
import {
|
import {
|
||||||
HeadContent,
|
HeadContent,
|
||||||
Navigate,
|
Navigate,
|
||||||
@@ -17,9 +14,12 @@ import Providers from "@/features/core/components/providers";
|
|||||||
import { ColorSchemeScript, mantineHtmlProps } from "@mantine/core";
|
import { ColorSchemeScript, mantineHtmlProps } from "@mantine/core";
|
||||||
import { HeaderConfig } from "@/features/core/types/header-config";
|
import { HeaderConfig } from "@/features/core/types/header-config";
|
||||||
import { playerQueries } from "@/features/players/queries";
|
import { playerQueries } from "@/features/players/queries";
|
||||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
|
||||||
import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure";
|
import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure";
|
||||||
import FullScreenLoader from "@/components/full-screen-loader";
|
import FullScreenLoader from "@/components/full-screen-loader";
|
||||||
|
import mantineCssUrl from '@mantine/core/styles.css?url'
|
||||||
|
import mantineDatesCssUrl from '@mantine/dates/styles.css?url'
|
||||||
|
import mantineCarouselCssUrl from '@mantine/carousel/styles.css?url'
|
||||||
|
import mantineTiptapCssUrl from '@mantine/tiptap/styles.css?url'
|
||||||
|
|
||||||
export const Route = createRootRouteWithContext<{
|
export const Route = createRootRouteWithContext<{
|
||||||
queryClient: QueryClient;
|
queryClient: QueryClient;
|
||||||
@@ -60,12 +60,18 @@ export const Route = createRootRouteWithContext<{
|
|||||||
},
|
},
|
||||||
{ rel: "manifest", href: "/site.webmanifest" },
|
{ rel: "manifest", href: "/site.webmanifest" },
|
||||||
{ rel: "icon", href: "/favicon.ico" },
|
{ rel: "icon", href: "/favicon.ico" },
|
||||||
|
{ rel: 'stylesheet', href: mantineCssUrl },
|
||||||
|
{ rel: 'stylesheet', href: mantineCarouselCssUrl },
|
||||||
|
{ rel: 'stylesheet', href: mantineDatesCssUrl },
|
||||||
|
{ rel: 'stylesheet', href: mantineTiptapCssUrl }
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
errorComponent: (props) => {
|
errorComponent: (props) => {
|
||||||
return (
|
return (
|
||||||
<RootDocument>
|
<RootDocument>
|
||||||
|
<Providers>
|
||||||
<DefaultCatchBoundary {...props} />
|
<DefaultCatchBoundary {...props} />
|
||||||
|
</Providers>
|
||||||
</RootDocument>
|
</RootDocument>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -77,12 +83,20 @@ export const Route = createRootRouteWithContext<{
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (location.pathname === '/login' || location.pathname === '/logout') {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
// https://github.com/TanStack/router/discussions/3531
|
// https://github.com/TanStack/router/discussions/3531
|
||||||
const auth = await ensureServerQueryData(
|
const auth = await ensureServerQueryData(
|
||||||
context.queryClient,
|
context.queryClient,
|
||||||
playerQueries.auth()
|
playerQueries.auth()
|
||||||
);
|
);
|
||||||
return { auth };
|
return { auth };
|
||||||
|
} catch (error) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
pendingComponent: () => <Providers><FullScreenLoader /></Providers>,
|
pendingComponent: () => <Providers><FullScreenLoader /></Providers>,
|
||||||
});
|
});
|
||||||
@@ -128,7 +142,6 @@ function RootDocument({ children }: { children: React.ReactNode }) {
|
|||||||
>
|
>
|
||||||
<div className="app">{children}</div>
|
<div className="app">{children}</div>
|
||||||
<Scripts />
|
<Scripts />
|
||||||
<ReactQueryDevtools />
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { redirect, createFileRoute, Outlet } from "@tanstack/react-router";
|
import { redirect, createFileRoute, Outlet } from "@tanstack/react-router";
|
||||||
import Layout from "@/features/core/components/layout";
|
import Layout from "@/features/core/components/layout";
|
||||||
import { useServerEvents } from "@/hooks/use-server-events";
|
import { useServerEvents } from "@/hooks/use-server-events";
|
||||||
import { Loader } from "@mantine/core";
|
import { Flex, Loader } from "@mantine/core";
|
||||||
|
|
||||||
export const Route = createFileRoute("/_authed")({
|
export const Route = createFileRoute("/_authed")({
|
||||||
beforeLoad: ({ context }) => {
|
beforeLoad: ({ context }) => {
|
||||||
@@ -26,7 +26,9 @@ export const Route = createFileRoute("/_authed")({
|
|||||||
},
|
},
|
||||||
pendingComponent: () => (
|
pendingComponent: () => (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Loader size="xl" />
|
<Flex w='100%' h="40dvh" justify="center" align="flex-end">
|
||||||
|
<Loader size='xl' />
|
||||||
|
</Flex>
|
||||||
</Layout>
|
</Layout>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { PreviewBracket } from "@/features/bracket/components/preview";
|
import BracketPreview from "@/features/admin/components/preview";
|
||||||
|
import { NumberInput } from "@mantine/core";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
export const Route = createFileRoute("/_authed/admin/preview")({
|
export const Route = createFileRoute("/_authed/admin/preview")({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
@@ -9,9 +11,25 @@ export const Route = createFileRoute("/_authed/admin/preview")({
|
|||||||
title: "Bracket Preview",
|
title: "Bracket Preview",
|
||||||
},
|
},
|
||||||
withPadding: false,
|
withPadding: false,
|
||||||
|
fullWidth: true,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
return <PreviewBracket />;
|
const [n, setN] = useState(16);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NumberInput
|
||||||
|
min={9}
|
||||||
|
max={27}
|
||||||
|
label="Number of teams"
|
||||||
|
value={n}
|
||||||
|
onChange={(value) => setN(value as number)}
|
||||||
|
w={150}
|
||||||
|
mb={2}
|
||||||
|
mx="md"
|
||||||
|
/>
|
||||||
|
<BracketPreview n={n} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { tournamentQueries } from "@/features/tournaments/queries";
|
|||||||
import ManageTournament from "@/features/tournaments/components/manage-tournament";
|
import ManageTournament from "@/features/tournaments/components/manage-tournament";
|
||||||
import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure";
|
import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure";
|
||||||
|
|
||||||
export const Route = createFileRoute("/_authed/admin/tournaments/$id")({
|
export const Route = createFileRoute("/_authed/admin/tournaments/$id/")({
|
||||||
beforeLoad: async ({ context, params }) => {
|
beforeLoad: async ({ context, params }) => {
|
||||||
const { queryClient } = context;
|
const { queryClient } = context;
|
||||||
const tournament = await ensureServerQueryData(
|
const tournament = await ensureServerQueryData(
|
||||||
32
src/app/routes/_authed/admin/tournaments/$id/teams.tsx
Normal file
32
src/app/routes/_authed/admin/tournaments/$id/teams.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||||
|
import { tournamentQueries } from "@/features/tournaments/queries";
|
||||||
|
import ManageTeams from "@/features/teams/components/manage-teams";
|
||||||
|
import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/_authed/admin/tournaments/$id/teams")({
|
||||||
|
beforeLoad: async ({ context, params }) => {
|
||||||
|
const { queryClient } = context;
|
||||||
|
const tournament = await ensureServerQueryData(
|
||||||
|
queryClient,
|
||||||
|
tournamentQueries.details(params.id)
|
||||||
|
);
|
||||||
|
if (!tournament) throw redirect({ to: "/admin/tournaments" });
|
||||||
|
return {
|
||||||
|
tournament,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
loader: ({ context }) => ({
|
||||||
|
header: {
|
||||||
|
withBackButton: true,
|
||||||
|
title: `${context.tournament.name} Teams`,
|
||||||
|
},
|
||||||
|
withPadding: false,
|
||||||
|
}),
|
||||||
|
component: RouteComponent,
|
||||||
|
});
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
const { id } = Route.useParams();
|
||||||
|
const { tournament } = Route.useRouteContext();
|
||||||
|
return <ManageTeams tournament={tournament} />;
|
||||||
|
}
|
||||||
@@ -1,31 +1,90 @@
|
|||||||
import { createFileRoute, redirect } from '@tanstack/react-router'
|
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||||
import { tournamentQueries } from '@/features/tournaments/queries'
|
import {
|
||||||
import { ensureServerQueryData } from '@/lib/tanstack-query/utils/ensure'
|
tournamentQueries,
|
||||||
import RunTournament from '@/features/tournaments/components/run-tournament'
|
useTournament,
|
||||||
|
} from "@/features/tournaments/queries";
|
||||||
|
import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure";
|
||||||
|
import SeedTournament from "@/features/tournaments/components/seed-tournament";
|
||||||
|
import { Container } from "@mantine/core";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { BracketData } from "@/features/bracket/types";
|
||||||
|
import { Match } from "@/features/matches/types";
|
||||||
|
import BracketView from "@/features/bracket/components/bracket-view";
|
||||||
|
import { SpotifyControlsBar } from "@/features/spotify/components";
|
||||||
|
|
||||||
export const Route = createFileRoute('/_authed/admin/tournaments/run/$id')({
|
export const Route = createFileRoute("/_authed/admin/tournaments/run/$id")({
|
||||||
beforeLoad: async ({ context, params }) => {
|
beforeLoad: async ({ context, params }) => {
|
||||||
const { queryClient } = context
|
const { queryClient } = context;
|
||||||
const tournament = await ensureServerQueryData(
|
const tournament = await ensureServerQueryData(
|
||||||
queryClient,
|
queryClient,
|
||||||
tournamentQueries.details(params.id)
|
tournamentQueries.details(params.id)
|
||||||
)
|
);
|
||||||
if (!tournament) throw redirect({ to: '/admin/tournaments' })
|
if (!tournament) throw redirect({ to: "/admin/tournaments" });
|
||||||
return {
|
return {
|
||||||
tournament,
|
tournament,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
loader: ({ context }) => ({
|
loader: ({ context }) => ({
|
||||||
fullWidth: true,
|
fullWidth: true,
|
||||||
|
withPadding: false,
|
||||||
|
showSpotifyPanel: true,
|
||||||
header: {
|
header: {
|
||||||
withBackButton: true,
|
withBackButton: true,
|
||||||
title: `Run ${context.tournament.name}`,
|
title: `Run ${context.tournament.name}`,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
})
|
});
|
||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
const { id } = Route.useParams()
|
const { id } = Route.useParams();
|
||||||
return <RunTournament tournamentId={id} />
|
const { data: tournament } = useTournament(id);
|
||||||
|
|
||||||
|
const bracket: BracketData = useMemo(() => {
|
||||||
|
if (!tournament.matches || tournament.matches.length === 0) {
|
||||||
|
return { winners: [], losers: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const winnersMap = new Map<number, Match[]>();
|
||||||
|
const losersMap = new Map<number, Match[]>();
|
||||||
|
|
||||||
|
tournament.matches
|
||||||
|
.sort((a, b) => a.lid - b.lid)
|
||||||
|
.forEach((match) => {
|
||||||
|
if (!match.is_losers_bracket) {
|
||||||
|
if (!winnersMap.has(match.round)) {
|
||||||
|
winnersMap.set(match.round, []);
|
||||||
|
}
|
||||||
|
winnersMap.get(match.round)!.push(match);
|
||||||
|
} else {
|
||||||
|
if (!losersMap.has(match.round)) {
|
||||||
|
losersMap.set(match.round, []);
|
||||||
|
}
|
||||||
|
losersMap.get(match.round)!.push(match);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const winners = Array.from(winnersMap.entries())
|
||||||
|
.sort(([a], [b]) => a - b)
|
||||||
|
.map(([, matches]) => matches);
|
||||||
|
|
||||||
|
const losers = Array.from(losersMap.entries())
|
||||||
|
.sort(([a], [b]) => a - b)
|
||||||
|
.map(([, matches]) => matches);
|
||||||
|
return { winners, losers };
|
||||||
|
}, [tournament.matches]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container size="md" px={0}>
|
||||||
|
<SpotifyControlsBar />
|
||||||
|
{tournament.matches?.length ? (
|
||||||
|
<BracketView bracket={bracket} showControls />
|
||||||
|
) : (
|
||||||
|
<SeedTournament
|
||||||
|
tournamentId={tournament.id}
|
||||||
|
teams={tournament.teams || []}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,35 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { TrophyIcon } from "@phosphor-icons/react";
|
import { tournamentQueries, useCurrentTournament } from "@/features/tournaments/queries";
|
||||||
import ListLink from "@/components/list-link";
|
import UpcomingTournament from "@/features/tournaments/components/upcoming-tournament";
|
||||||
import { Box, Divider, Text } from "@mantine/core";
|
import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure";
|
||||||
|
import StartedTournament from "@/features/tournaments/components/started-tournament";
|
||||||
|
import { Suspense } from "react";
|
||||||
|
import UpcomingTournamentSkeleton from "@/features/tournaments/components/upcoming-tournament/skeleton";
|
||||||
|
|
||||||
export const Route = createFileRoute("/_authed/")({
|
export const Route = createFileRoute("/_authed/")({
|
||||||
component: Home,
|
component: () => <Suspense fallback={<UpcomingTournamentSkeleton />}>
|
||||||
loader: () => ({
|
<Home />
|
||||||
withPadding: false
|
</Suspense>,
|
||||||
})
|
beforeLoad: async ({ context }) => {
|
||||||
|
const queryClient = context.queryClient;
|
||||||
|
const tournament = await ensureServerQueryData(queryClient, tournamentQueries.current())
|
||||||
|
|
||||||
|
return { tournament }
|
||||||
|
},
|
||||||
|
loader: ({ context }) => ({
|
||||||
|
withPadding: false,
|
||||||
|
header: {
|
||||||
|
title: context.tournament.name || "FLXN"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
pendingComponent: () => <UpcomingTournamentSkeleton />
|
||||||
});
|
});
|
||||||
|
|
||||||
function Home() {
|
function Home() {
|
||||||
return (
|
const { data: tournament } = useCurrentTournament();
|
||||||
<>
|
if (!tournament.matches || tournament.matches.length === 0) {
|
||||||
<Box h='60vh' p="md">
|
return <UpcomingTournament tournament={tournament} />;
|
||||||
<Text m='16vh' fw={500}>Some Content Here</Text>
|
}
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box>
|
return <StartedTournament tournament={tournament} />
|
||||||
<Text pl='md'>Quick Links</Text>
|
|
||||||
<Divider />
|
|
||||||
<ListLink label="All Tournaments" to="/tournaments" Icon={TrophyIcon} />
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import Profile from "@/features/players/components/profile";
|
import Profile from "@/features/players/components/profile";
|
||||||
import { playerQueries } from "@/features/players/queries";
|
import HeaderSkeleton from "@/features/players/components/profile/header-skeleton";
|
||||||
|
import ProfileSkeleton from "@/features/players/components/profile/skeleton";
|
||||||
|
import { playerKeys, playerQueries } from "@/features/players/queries";
|
||||||
import { prefetchServerQuery } from "@/lib/tanstack-query/utils/prefetch";
|
import { prefetchServerQuery } from "@/lib/tanstack-query/utils/prefetch";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { Suspense } from "react";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const searchSchema = z.object({
|
const searchSchema = z.object({
|
||||||
@@ -12,10 +15,16 @@ export const Route = createFileRoute("/_authed/profile/$playerId")({
|
|||||||
validateSearch: searchSchema,
|
validateSearch: searchSchema,
|
||||||
beforeLoad: async ({ params, context }) => {
|
beforeLoad: async ({ params, context }) => {
|
||||||
const { queryClient } = context;
|
const { queryClient } = context;
|
||||||
await prefetchServerQuery(
|
await Promise.all([
|
||||||
|
prefetchServerQuery(
|
||||||
queryClient,
|
queryClient,
|
||||||
playerQueries.details(params.playerId)
|
playerQueries.details(params.playerId)
|
||||||
);
|
),
|
||||||
|
prefetchServerQuery(
|
||||||
|
queryClient,
|
||||||
|
playerQueries.matches(params.playerId)
|
||||||
|
),
|
||||||
|
]);
|
||||||
},
|
},
|
||||||
loader: ({ params, context }) => ({
|
loader: ({ params, context }) => ({
|
||||||
header: {
|
header: {
|
||||||
@@ -24,10 +33,13 @@ export const Route = createFileRoute("/_authed/profile/$playerId")({
|
|||||||
settingsLink:
|
settingsLink:
|
||||||
context?.auth.user.id === params.playerId ? "/settings" : undefined,
|
context?.auth.user.id === params.playerId ? "/settings" : undefined,
|
||||||
},
|
},
|
||||||
refresh: [playerQueries.details(params.playerId).queryKey],
|
withPadding: false,
|
||||||
|
refresh: [playerKeys.details(params.playerId), playerKeys.matches(params.playerId), playerKeys.stats(params.playerId)],
|
||||||
}),
|
}),
|
||||||
component: () => {
|
component: () => {
|
||||||
const { playerId } = Route.useParams();
|
const { playerId } = Route.useParams();
|
||||||
return <Profile id={playerId} />;
|
return <Suspense fallback={<ProfileSkeleton />}>
|
||||||
|
<Profile id={playerId} />
|
||||||
|
</Suspense>;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
28
src/app/routes/_authed/stats.tsx
Normal file
28
src/app/routes/_authed/stats.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { playerQueries } from "@/features/players/queries";
|
||||||
|
import PlayerStatsTable from "@/features/players/components/player-stats-table";
|
||||||
|
import { Suspense } from "react";
|
||||||
|
import PlayerStatsTableSkeleton from "@/features/players/components/player-stats-table-skeleton";
|
||||||
|
import { prefetchServerQuery } from "@/lib/tanstack-query/utils/prefetch";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/_authed/stats")({
|
||||||
|
component: Stats,
|
||||||
|
beforeLoad: ({ context }) => {
|
||||||
|
const queryClient = context.queryClient;
|
||||||
|
prefetchServerQuery(queryClient, playerQueries.allStats());
|
||||||
|
},
|
||||||
|
loader: () => ({
|
||||||
|
withPadding: false,
|
||||||
|
fullWidth: true,
|
||||||
|
header: {
|
||||||
|
title: "Player Stats"
|
||||||
|
},
|
||||||
|
refresh: [playerQueries.allStats().queryKey],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
function Stats() {
|
||||||
|
return <Suspense fallback={<PlayerStatsTableSkeleton />}>
|
||||||
|
<PlayerStatsTable />
|
||||||
|
</Suspense>;
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import TeamProfile from "@/features/teams/components/team-profile";
|
import TeamProfile from "@/features/teams/components/team-profile";
|
||||||
import { teamQueries } from "@/features/teams/queries";
|
import ProfileSkeleton from "@/features/teams/components/team-profile/skeleton";
|
||||||
import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure";
|
import { teamKeys, teamQueries } from "@/features/teams/queries";
|
||||||
import { prefetchServerQuery } from "@/lib/tanstack-query/utils/prefetch";
|
import { prefetchServerQuery } from "@/lib/tanstack-query/utils/prefetch";
|
||||||
import { redirect, createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { Suspense } from "react";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const searchSchema = z.object({
|
const searchSchema = z.object({
|
||||||
@@ -20,10 +21,13 @@ export const Route = createFileRoute("/_authed/teams/$teamId")({
|
|||||||
collapsed: true,
|
collapsed: true,
|
||||||
withBackButton: true,
|
withBackButton: true,
|
||||||
},
|
},
|
||||||
refresh: [teamQueries.details(params.teamId).queryKey],
|
refresh: [teamKeys.details(params.teamId), teamKeys.matches(params.teamId), teamKeys.stats(params.teamId)],
|
||||||
|
withPadding: false
|
||||||
}),
|
}),
|
||||||
component: () => {
|
component: () => {
|
||||||
const { teamId } = Route.useParams();
|
const { teamId } = Route.useParams();
|
||||||
return <TeamProfile id={teamId} />;
|
return <Suspense fallback={<ProfileSkeleton />}>
|
||||||
|
<TeamProfile id={teamId} />
|
||||||
|
</Suspense>;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
82
src/app/routes/_authed/tournaments/$id.bracket.tsx
Normal file
82
src/app/routes/_authed/tournaments/$id.bracket.tsx
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||||
|
import {
|
||||||
|
tournamentQueries,
|
||||||
|
useTournament,
|
||||||
|
} from "@/features/tournaments/queries";
|
||||||
|
import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure";
|
||||||
|
import SeedTournament from "@/features/tournaments/components/seed-tournament";
|
||||||
|
import { Container } from "@mantine/core";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { BracketData } from "@/features/bracket/types";
|
||||||
|
import { Match } from "@/features/matches/types";
|
||||||
|
import BracketView from "@/features/bracket/components/bracket-view";
|
||||||
|
import { SpotifyControlsBar } from "@/features/spotify/components";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/_authed/tournaments/$id/bracket")({
|
||||||
|
beforeLoad: async ({ context, params }) => {
|
||||||
|
const { queryClient } = context;
|
||||||
|
const tournament = await ensureServerQueryData(
|
||||||
|
queryClient,
|
||||||
|
tournamentQueries.details(params.id)
|
||||||
|
);
|
||||||
|
if (!tournament) throw redirect({ to: "/admin/tournaments" });
|
||||||
|
return {
|
||||||
|
tournament,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
loader: ({ context }) => ({
|
||||||
|
fullWidth: true,
|
||||||
|
withPadding: false,
|
||||||
|
showSpotifyPanel: true,
|
||||||
|
header: {
|
||||||
|
withBackButton: true,
|
||||||
|
title: `${context.tournament.name}`,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
component: RouteComponent,
|
||||||
|
});
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
const { id } = Route.useParams();
|
||||||
|
const { data: tournament } = useTournament(id);
|
||||||
|
|
||||||
|
const bracket: BracketData = useMemo(() => {
|
||||||
|
if (!tournament.matches || tournament.matches.length === 0) {
|
||||||
|
return { winners: [], losers: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const winnersMap = new Map<number, Match[]>();
|
||||||
|
const losersMap = new Map<number, Match[]>();
|
||||||
|
|
||||||
|
tournament.matches
|
||||||
|
.sort((a, b) => a.lid - b.lid)
|
||||||
|
.forEach((match) => {
|
||||||
|
if (!match.is_losers_bracket) {
|
||||||
|
if (!winnersMap.has(match.round)) {
|
||||||
|
winnersMap.set(match.round, []);
|
||||||
|
}
|
||||||
|
winnersMap.get(match.round)!.push(match);
|
||||||
|
} else {
|
||||||
|
if (!losersMap.has(match.round)) {
|
||||||
|
losersMap.set(match.round, []);
|
||||||
|
}
|
||||||
|
losersMap.get(match.round)!.push(match);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const winners = Array.from(winnersMap.entries())
|
||||||
|
.sort(([a], [b]) => a - b)
|
||||||
|
.map(([, matches]) => matches);
|
||||||
|
|
||||||
|
const losers = Array.from(losersMap.entries())
|
||||||
|
.sort(([a], [b]) => a - b)
|
||||||
|
.map(([, matches]) => matches);
|
||||||
|
return { winners, losers };
|
||||||
|
}, [tournament.matches]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container size="md" px={0}>
|
||||||
|
<BracketView bracket={bracket} />
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ import { tournamentQueries } from '@/features/tournaments/queries';
|
|||||||
import Profile from '@/features/tournaments/components/profile';
|
import Profile from '@/features/tournaments/components/profile';
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { prefetchServerQuery } from '@/lib/tanstack-query/utils/prefetch';
|
import { prefetchServerQuery } from '@/lib/tanstack-query/utils/prefetch';
|
||||||
|
import { Suspense } from 'react';
|
||||||
|
import ProfileSkeleton from '@/features/tournaments/components/profile/skeleton';
|
||||||
|
|
||||||
const searchSchema = z.object({
|
const searchSchema = z.object({
|
||||||
tab: z.string().optional(),
|
tab: z.string().optional(),
|
||||||
@@ -10,9 +12,9 @@ const searchSchema = z.object({
|
|||||||
|
|
||||||
export const Route = createFileRoute('/_authed/tournaments/$tournamentId')({
|
export const Route = createFileRoute('/_authed/tournaments/$tournamentId')({
|
||||||
validateSearch: searchSchema,
|
validateSearch: searchSchema,
|
||||||
beforeLoad: async ({ context, params }) => {
|
beforeLoad: ({ context, params }) => {
|
||||||
const { queryClient } = context;
|
const { queryClient } = context;
|
||||||
await prefetchServerQuery(queryClient, tournamentQueries.details(params.tournamentId))
|
prefetchServerQuery(queryClient, tournamentQueries.details(params.tournamentId))
|
||||||
},
|
},
|
||||||
loader: ({ params, context }) => ({
|
loader: ({ params, context }) => ({
|
||||||
header: {
|
header: {
|
||||||
@@ -20,7 +22,7 @@ export const Route = createFileRoute('/_authed/tournaments/$tournamentId')({
|
|||||||
withBackButton: true,
|
withBackButton: true,
|
||||||
settingsLink: context.auth.roles.includes("Admin") ? `/admin/tournaments/${params.tournamentId}` : undefined
|
settingsLink: context.auth.roles.includes("Admin") ? `/admin/tournaments/${params.tournamentId}` : undefined
|
||||||
},
|
},
|
||||||
refresh: tournamentQueries.details(params.tournamentId).queryKey,
|
refresh: [tournamentQueries.details(params.tournamentId).queryKey],
|
||||||
withPadding: false
|
withPadding: false
|
||||||
}),
|
}),
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
@@ -28,5 +30,7 @@ export const Route = createFileRoute('/_authed/tournaments/$tournamentId')({
|
|||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
const tournamentId = Route.useParams().tournamentId;
|
const tournamentId = Route.useParams().tournamentId;
|
||||||
return <Profile id={tournamentId} />
|
return <Suspense fallback={<ProfileSkeleton />}>
|
||||||
|
<Profile id={tournamentId} />
|
||||||
|
</Suspense>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,14 @@
|
|||||||
import Page from '@/components/page'
|
|
||||||
import { Stack } from '@mantine/core'
|
|
||||||
import { createFileRoute } from '@tanstack/react-router'
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
import { TournamentCard } from '@/features/tournaments/components/tournament-card'
|
import { tournamentQueries } from '@/features/tournaments/queries'
|
||||||
import { tournamentQueries, useTournaments } from '@/features/tournaments/queries'
|
|
||||||
import { useAuth } from '@/contexts/auth-context'
|
|
||||||
import { useSheet } from '@/hooks/use-sheet'
|
|
||||||
import Sheet from '@/components/sheet/sheet'
|
|
||||||
import TournamentForm from '@/features/tournaments/components/tournament-form'
|
|
||||||
import { PlusIcon } from '@phosphor-icons/react'
|
|
||||||
import Button from '@/components/button'
|
|
||||||
import { prefetchServerQuery } from '@/lib/tanstack-query/utils/prefetch'
|
import { prefetchServerQuery } from '@/lib/tanstack-query/utils/prefetch'
|
||||||
|
import { Suspense } from 'react'
|
||||||
|
import TournamentCardList from '@/features/tournaments/components/tournament-card-list'
|
||||||
|
import { Skeleton, Stack } from '@mantine/core'
|
||||||
|
|
||||||
export const Route = createFileRoute('/_authed/tournaments/')({
|
export const Route = createFileRoute('/_authed/tournaments/')({
|
||||||
beforeLoad: async ({ context }) => {
|
beforeLoad: async ({ context }) => {
|
||||||
const { queryClient } = context;
|
const { queryClient } = context;
|
||||||
await prefetchServerQuery(queryClient, tournamentQueries.list())
|
prefetchServerQuery(queryClient, tournamentQueries.list())
|
||||||
},
|
},
|
||||||
loader: () => ({
|
loader: () => ({
|
||||||
header: {
|
header: {
|
||||||
@@ -27,27 +21,11 @@ export const Route = createFileRoute('/_authed/tournaments/')({
|
|||||||
})
|
})
|
||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
const { data: tournaments } = useTournaments();
|
return <Suspense fallback={<Stack gap="md">
|
||||||
const { roles } = useAuth();
|
{Array(10).fill(null).map((_, index) => (
|
||||||
const sheet = useSheet();
|
<Skeleton height="120px" w="100%" />
|
||||||
|
))}
|
||||||
return <Page>
|
</Stack>}>
|
||||||
<Stack>
|
<TournamentCardList />
|
||||||
{
|
</Suspense>
|
||||||
roles?.includes("Admin") ? (
|
|
||||||
<>
|
|
||||||
<Button leftSection={<PlusIcon />} variant='subtle' onClick={sheet.open}>Create Tournament</Button>
|
|
||||||
<Sheet {...sheet.props} title='Create Tournament'>
|
|
||||||
<TournamentForm close={sheet.close} />
|
|
||||||
</Sheet>
|
|
||||||
</>
|
|
||||||
) : null
|
|
||||||
}
|
|
||||||
{
|
|
||||||
tournaments?.map((tournament: any) => (
|
|
||||||
<TournamentCard key={tournament.id} tournament={tournament} />
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</Stack>
|
|
||||||
</Page>
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// API file that handles all supertokens auth routes
|
// API file that handles all supertokens auth routes
|
||||||
import { createServerFileRoute } from '@tanstack/react-start/server';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import { handleAuthAPIRequest } from 'supertokens-node/custom'
|
import { handleAuthAPIRequest } from 'supertokens-node/custom'
|
||||||
import { ensureSuperTokensBackend } from '@/lib/supertokens/server'
|
import { ensureSuperTokensBackend } from '@/lib/supertokens/server'
|
||||||
|
|
||||||
@@ -9,10 +9,12 @@ ensureSuperTokensBackend();
|
|||||||
const superTokensHandler = handleAuthAPIRequest();
|
const superTokensHandler = handleAuthAPIRequest();
|
||||||
const handleRequest = async ({ request }: {request: Request}) => {
|
const handleRequest = async ({ request }: {request: Request}) => {
|
||||||
console.log("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=");
|
console.log("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=");
|
||||||
console.log("Handling auth request:", request);
|
console.log("Handling auth request:", request.method, request.url);
|
||||||
return superTokensHandler(request);
|
return superTokensHandler(request);
|
||||||
};
|
};
|
||||||
export const ServerRoute = createServerFileRoute('/api/auth/$').methods({
|
export const Route = createFileRoute('/api/auth/$')({
|
||||||
|
server: {
|
||||||
|
handlers: {
|
||||||
GET: handleRequest,
|
GET: handleRequest,
|
||||||
POST: handleRequest,
|
POST: handleRequest,
|
||||||
PUT: handleRequest,
|
PUT: handleRequest,
|
||||||
@@ -20,4 +22,6 @@ export const ServerRoute = createServerFileRoute('/api/auth/$').methods({
|
|||||||
PATCH: handleRequest,
|
PATCH: handleRequest,
|
||||||
OPTIONS: handleRequest,
|
OPTIONS: handleRequest,
|
||||||
HEAD: handleRequest,
|
HEAD: handleRequest,
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
import { createServerFileRoute } from "@tanstack/react-start/server";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { serverEvents, type ServerEvent } from "@/lib/events/emitter";
|
import { serverEvents, type ServerEvent } from "@/lib/events/emitter";
|
||||||
import { logger } from "@/lib/logger";
|
import { logger } from "@/lib/logger";
|
||||||
import { superTokensRequestMiddleware } from "@/utils/supertokens";
|
import { superTokensRequestMiddleware } from "@/utils/supertokens";
|
||||||
|
|
||||||
export const ServerRoute = createServerFileRoute("/api/events/$").middleware([superTokensRequestMiddleware]).methods({
|
export const Route = createFileRoute("/api/events/$")({
|
||||||
|
server: {
|
||||||
|
middleware: [superTokensRequestMiddleware],
|
||||||
|
handlers: {
|
||||||
GET: ({ request, context }) => {
|
GET: ({ request, context }) => {
|
||||||
logger.info('ServerEvents | New connection', context?.userAuthId);
|
|
||||||
|
|
||||||
const stream = new ReadableStream({
|
const stream = new ReadableStream({
|
||||||
start(controller) {
|
start(controller) {
|
||||||
// Send initial connection messages
|
|
||||||
const connectMessage = `data: ${JSON.stringify({ type: "connected" })}\n\n`;
|
const connectMessage = `data: ${JSON.stringify({ type: "connected" })}\n\n`;
|
||||||
controller.enqueue(new TextEncoder().encode(connectMessage));
|
controller.enqueue(new TextEncoder().encode(connectMessage));
|
||||||
|
|
||||||
// Listen for events and broadcast to all connections
|
|
||||||
const handleEvent = (event: ServerEvent) => {
|
const handleEvent = (event: ServerEvent) => {
|
||||||
logger.info('ServerEvents | Event received', event);
|
logger.info("ServerEvents | Event received", event);
|
||||||
const message = `data: ${JSON.stringify(event)}\n\n`;
|
const message = `data: ${JSON.stringify(event)}\n\n`;
|
||||||
try {
|
try {
|
||||||
controller.enqueue(new TextEncoder().encode(message));
|
controller.enqueue(new TextEncoder().encode(message));
|
||||||
@@ -25,31 +24,24 @@ export const ServerRoute = createServerFileRoute("/api/events/$").middleware([su
|
|||||||
};
|
};
|
||||||
|
|
||||||
serverEvents.on("test", handleEvent);
|
serverEvents.on("test", handleEvent);
|
||||||
|
serverEvents.on("match", handleEvent);
|
||||||
|
serverEvents.on("reaction", handleEvent);
|
||||||
|
|
||||||
// Keep alive ping every 30 seconds
|
|
||||||
const pingInterval = setInterval(() => {
|
const pingInterval = setInterval(() => {
|
||||||
try {
|
try {
|
||||||
const pingMessage = `data: ${JSON.stringify({ type: "ping" })}\n\n`;
|
const pingMessage = `data: ${JSON.stringify({ type: "ping" })}\n\n`;
|
||||||
controller.enqueue(new TextEncoder().encode(pingMessage));
|
controller.enqueue(new TextEncoder().encode(pingMessage));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
clearInterval(pingInterval);
|
clearInterval(pingInterval);
|
||||||
controller.close();
|
|
||||||
}
|
}
|
||||||
}, 30000);
|
}, 30000);
|
||||||
|
|
||||||
const cleanup = () => {
|
const cleanup = () => {
|
||||||
serverEvents.off("test", handleEvent);
|
serverEvents.off("test", handleEvent);
|
||||||
clearInterval(pingInterval);
|
clearInterval(pingInterval);
|
||||||
try {
|
|
||||||
logger.info('ServerEvents | Closing connection', context?.userAuthId);
|
|
||||||
controller.close();
|
|
||||||
} catch (e) {
|
|
||||||
logger.error('ServerEvents | Error closing controller', e);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
request.signal?.addEventListener("abort", cleanup);
|
request.signal?.addEventListener("abort", cleanup);
|
||||||
|
|
||||||
return cleanup;
|
return cleanup;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -64,4 +56,6 @@ export const ServerRoute = createServerFileRoute("/api/events/$").middleware([su
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
@@ -1,84 +1,100 @@
|
|||||||
import { createServerFileRoute } from "@tanstack/react-start/server";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { logger } from "@/lib/logger";
|
import { logger } from "@/lib/logger";
|
||||||
|
|
||||||
export const ServerRoute = createServerFileRoute("/api/files/$collection/$recordId/$file").methods({
|
export const Route = createFileRoute(
|
||||||
|
"/api/files/$collection/$recordId/$file"
|
||||||
|
)({
|
||||||
|
server: {
|
||||||
|
handlers: {
|
||||||
GET: async ({ params, request }) => {
|
GET: async ({ params, request }) => {
|
||||||
try {
|
try {
|
||||||
const { collection, recordId, file } = params;
|
const { collection, recordId, file } = params;
|
||||||
const pocketbaseUrl = process.env.VITE_POCKETBASE_URL || 'http://127.0.0.1:8090';
|
const pocketbaseUrl =
|
||||||
|
process.env.POCKETBASE_URL || "http://127.0.0.1:8090";
|
||||||
const fileUrl = `${pocketbaseUrl}/api/files/${collection}/${recordId}/${file}`;
|
const fileUrl = `${pocketbaseUrl}/api/files/${collection}/${recordId}/${file}`;
|
||||||
|
|
||||||
logger.info('File proxy', {
|
logger.info("File proxy", {
|
||||||
collection,
|
collection,
|
||||||
recordId,
|
recordId,
|
||||||
file,
|
file,
|
||||||
targetUrl: fileUrl
|
targetUrl: fileUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await fetch(fileUrl, {
|
const response = await fetch(fileUrl, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
...(request.headers.get('range') && { 'Range': request.headers.get('range')! }),
|
...(request.headers.get("range") && {
|
||||||
...(request.headers.get('if-none-match') && { 'If-None-Match': request.headers.get('if-none-match')! }),
|
Range: request.headers.get("range")!,
|
||||||
...(request.headers.get('if-modified-since') && { 'If-Modified-Since': request.headers.get('if-modified-since')! }),
|
}),
|
||||||
|
...(request.headers.get("if-none-match") && {
|
||||||
|
"If-None-Match": request.headers.get("if-none-match")!,
|
||||||
|
}),
|
||||||
|
...(request.headers.get("if-modified-since") && {
|
||||||
|
"If-Modified-Since": request.headers.get("if-modified-since")!,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
logger.error('PocketBase file request failed', {
|
logger.error("PocketBase file request failed", {
|
||||||
status: response.status,
|
status: response.status,
|
||||||
statusText: response.statusText,
|
statusText: response.statusText,
|
||||||
url: fileUrl
|
url: fileUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
return new Response('File not found', { status: 404 });
|
return new Response("File not found", { status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Response(`PocketBase error: ${response.statusText}`, {
|
return new Response(`PocketBase error: ${response.statusText}`, {
|
||||||
status: response.status
|
status: response.status,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = response.body;
|
const body = response.body;
|
||||||
const responseHeaders = new Headers();
|
const responseHeaders = new Headers();
|
||||||
const headers = [
|
const headers = [
|
||||||
'content-type',
|
"content-type",
|
||||||
'content-length',
|
"content-length",
|
||||||
'content-disposition',
|
"content-disposition",
|
||||||
'etag',
|
"etag",
|
||||||
'last-modified',
|
"last-modified",
|
||||||
'cache-control',
|
"cache-control",
|
||||||
'accept-ranges',
|
"accept-ranges",
|
||||||
'content-range'
|
"content-range",
|
||||||
];
|
];
|
||||||
|
|
||||||
headers.forEach(header => {
|
headers.forEach((header) => {
|
||||||
const value = response.headers.get(header);
|
const value = response.headers.get(header);
|
||||||
if (value) {
|
if (value) {
|
||||||
responseHeaders.set(header, value);
|
responseHeaders.set(header, value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
responseHeaders.set('Access-Control-Allow-Origin', '*');
|
responseHeaders.set("Access-Control-Allow-Origin", "*");
|
||||||
responseHeaders.set('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
|
responseHeaders.set(
|
||||||
responseHeaders.set('Access-Control-Allow-Headers', 'Range, If-None-Match, If-Modified-Since');
|
"Access-Control-Allow-Methods",
|
||||||
|
"GET, HEAD, OPTIONS"
|
||||||
|
);
|
||||||
|
responseHeaders.set(
|
||||||
|
"Access-Control-Allow-Headers",
|
||||||
|
"Range, If-None-Match, If-Modified-Since"
|
||||||
|
);
|
||||||
|
|
||||||
logger.info('File proxy response', {
|
logger.info("File proxy response", {
|
||||||
status: response.status,
|
status: response.status,
|
||||||
contentType: response.headers.get('content-type'),
|
contentType: response.headers.get("content-type"),
|
||||||
contentLength: response.headers.get('content-length')
|
contentLength: response.headers.get("content-length"),
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Response(body, {
|
return new Response(body, {
|
||||||
status: response.status,
|
status: response.status,
|
||||||
statusText: response.statusText,
|
statusText: response.statusText,
|
||||||
headers: responseHeaders
|
headers: responseHeaders,
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('File proxy error', error);
|
logger.error("File proxy error", error);
|
||||||
return new Response('Internal server error', { status: 500 });
|
return new Response("Internal server error", { status: 500 });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -86,10 +102,12 @@ export const ServerRoute = createServerFileRoute("/api/files/$collection/$record
|
|||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: {
|
headers: {
|
||||||
'Access-Control-Allow-Origin': '*',
|
"Access-Control-Allow-Origin": "*",
|
||||||
'Access-Control-Allow-Methods': 'GET, OPTIONS',
|
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
||||||
'Access-Control-Max-Age': '86400',
|
"Access-Control-Max-Age": "86400",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
145
src/app/routes/api/spotify/callback.ts
Normal file
145
src/app/routes/api/spotify/callback.ts
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
const SPOTIFY_CLIENT_ID = process.env.VITE_SPOTIFY_CLIENT_ID!;
|
||||||
|
const SPOTIFY_CLIENT_SECRET = process.env.SPOTIFY_CLIENT_SECRET!;
|
||||||
|
const SPOTIFY_REDIRECT_URI = process.env.VITE_SPOTIFY_REDIRECT_URI!;
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/api/spotify/callback")({
|
||||||
|
server: {
|
||||||
|
handlers: {
|
||||||
|
GET: async ({ request }: { request: Request }) => {
|
||||||
|
const getReturnPath = (state: string | null): string => {
|
||||||
|
if (!state) return "/";
|
||||||
|
try {
|
||||||
|
const decodedState = JSON.parse(atob(state));
|
||||||
|
return decodedState.returnPath || "/";
|
||||||
|
} catch {
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const code = url.searchParams.get("code");
|
||||||
|
const state = url.searchParams.get("state");
|
||||||
|
const error = url.searchParams.get("error");
|
||||||
|
|
||||||
|
const returnPath = getReturnPath(state);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error("Spotify OAuth error:", error);
|
||||||
|
return new Response(null, {
|
||||||
|
status: 302,
|
||||||
|
headers: {
|
||||||
|
Location:
|
||||||
|
returnPath + "?spotify_error=" + encodeURIComponent(error),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!code || !state) {
|
||||||
|
console.error("Missing code or state:", {
|
||||||
|
code: !!code,
|
||||||
|
state: !!state,
|
||||||
|
});
|
||||||
|
return new Response(null, {
|
||||||
|
status: 302,
|
||||||
|
headers: {
|
||||||
|
Location: returnPath + "?spotify_error=missing_code_or_state",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Token exchange attempt:", {
|
||||||
|
client_id: SPOTIFY_CLIENT_ID,
|
||||||
|
redirect_uri: SPOTIFY_REDIRECT_URI,
|
||||||
|
has_code: !!code,
|
||||||
|
has_state: !!state,
|
||||||
|
});
|
||||||
|
|
||||||
|
const tokenResponse = await fetch(
|
||||||
|
"https://accounts.spotify.com/api/token",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
Authorization: `Basic ${Buffer.from(`${SPOTIFY_CLIENT_ID}:${SPOTIFY_CLIENT_SECRET}`).toString("base64")}`,
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({
|
||||||
|
grant_type: "authorization_code",
|
||||||
|
code,
|
||||||
|
redirect_uri: SPOTIFY_REDIRECT_URI,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!tokenResponse.ok) {
|
||||||
|
const errorText = await tokenResponse.text();
|
||||||
|
console.error("Token exchange error:", {
|
||||||
|
status: tokenResponse.status,
|
||||||
|
statusText: tokenResponse.statusText,
|
||||||
|
body: errorText,
|
||||||
|
redirect_uri: SPOTIFY_REDIRECT_URI,
|
||||||
|
});
|
||||||
|
|
||||||
|
const errorParam = encodeURIComponent(
|
||||||
|
`${tokenResponse.status}: ${errorText}`
|
||||||
|
);
|
||||||
|
return new Response(null, {
|
||||||
|
status: 302,
|
||||||
|
headers: {
|
||||||
|
Location: `${returnPath}?spotify_error=token_exchange_failed&details=${errorParam}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokens = await tokenResponse.json();
|
||||||
|
|
||||||
|
console.log("Token exchange successful:", {
|
||||||
|
has_access_token: !!tokens.access_token,
|
||||||
|
has_refresh_token: !!tokens.refresh_token,
|
||||||
|
expires_in: tokens.expires_in,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("Decoded return path:", returnPath);
|
||||||
|
|
||||||
|
const response = new Response(null, {
|
||||||
|
status: 302,
|
||||||
|
headers: {
|
||||||
|
Location: returnPath + "?spotify_auth=success",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const isSecure = import.meta.env.NODE_ENV === "production";
|
||||||
|
const cookieOptions = `HttpOnly; ${isSecure ? 'Secure; ' : ''}SameSite=Lax; Path=/; Max-Age=${tokens.expires_in}`;
|
||||||
|
|
||||||
|
response.headers.append(
|
||||||
|
"Set-Cookie",
|
||||||
|
`spotify_access_token=${tokens.access_token}; ${cookieOptions}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tokens.refresh_token) {
|
||||||
|
const refreshCookieOptions = `HttpOnly; ${isSecure ? 'Secure; ' : ''}SameSite=Lax; Path=/; Max-Age=${60 * 60 * 24 * 30}`; // 30 days
|
||||||
|
response.headers.append(
|
||||||
|
"Set-Cookie",
|
||||||
|
`spotify_refresh_token=${tokens.refresh_token}; ${refreshCookieOptions}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Spotify callback error:", error);
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const state = url.searchParams.get("state");
|
||||||
|
const returnPath = getReturnPath(state);
|
||||||
|
return new Response(null, {
|
||||||
|
status: 302,
|
||||||
|
headers: {
|
||||||
|
Location: returnPath + "?spotify_error=callback_failed",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
60
src/app/routes/api/spotify/capture.ts
Normal file
60
src/app/routes/api/spotify/capture.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { SpotifyWebApiClient } from "@/lib/spotify/client";
|
||||||
|
import type { SpotifyPlaybackSnapshot } from "@/lib/spotify/types";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/api/spotify/capture")({
|
||||||
|
server: {
|
||||||
|
handlers: {
|
||||||
|
POST: async ({ request }: { request: Request }) => {
|
||||||
|
try {
|
||||||
|
const cookies = request.headers.get("Cookie") || "";
|
||||||
|
const accessTokenMatch = cookies.match(
|
||||||
|
/spotify_access_token=([^;]+)/
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!accessTokenMatch) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "No access token found" }),
|
||||||
|
{
|
||||||
|
status: 401,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessToken = decodeURIComponent(accessTokenMatch[1]);
|
||||||
|
const spotifyClient = new SpotifyWebApiClient(accessToken);
|
||||||
|
|
||||||
|
const snapshot = await spotifyClient.createPlaybackSnapshot();
|
||||||
|
|
||||||
|
if (!snapshot) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "No active playback to capture" }),
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({ snapshot }), {
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Spotify capture error:", error);
|
||||||
|
|
||||||
|
const errorMessage =
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Failed to capture playback state";
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({ error: errorMessage }), {
|
||||||
|
status: 500,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
203
src/app/routes/api/spotify/playback.ts
Normal file
203
src/app/routes/api/spotify/playback.ts
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { SpotifyWebApiClient } from "@/lib/spotify/client";
|
||||||
|
|
||||||
|
function getAccessTokenFromCookies(request: Request): string | null {
|
||||||
|
const cookieHeader = request.headers.get("cookie");
|
||||||
|
if (!cookieHeader) return null;
|
||||||
|
|
||||||
|
const cookies = Object.fromEntries(
|
||||||
|
cookieHeader.split("; ").map((c) => c.split("="))
|
||||||
|
);
|
||||||
|
|
||||||
|
return cookies.spotify_access_token || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/api/spotify/playback")({
|
||||||
|
server: {
|
||||||
|
handlers: {
|
||||||
|
POST: async ({ request }: { request: Request }) => {
|
||||||
|
try {
|
||||||
|
const accessToken = getAccessTokenFromCookies(request);
|
||||||
|
if (!accessToken) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "No access token found" }),
|
||||||
|
{
|
||||||
|
status: 401,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = await request.json();
|
||||||
|
const { action, deviceId, volumePercent, trackId, positionMs } = body;
|
||||||
|
|
||||||
|
const spotifyClient = new SpotifyWebApiClient(accessToken);
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case "play":
|
||||||
|
await spotifyClient.play(deviceId);
|
||||||
|
break;
|
||||||
|
case "playTrack":
|
||||||
|
if (!trackId) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: "trackId is required for playTrack action",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await spotifyClient.playTrack(trackId, deviceId, positionMs);
|
||||||
|
break;
|
||||||
|
case "pause":
|
||||||
|
await spotifyClient.pause();
|
||||||
|
break;
|
||||||
|
case "next":
|
||||||
|
await spotifyClient.skipToNext();
|
||||||
|
break;
|
||||||
|
case "previous":
|
||||||
|
await spotifyClient.skipToPrevious();
|
||||||
|
break;
|
||||||
|
case "volume":
|
||||||
|
if (typeof volumePercent !== "number") {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "volumePercent must be a number" }),
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await spotifyClient.setVolume(volumePercent);
|
||||||
|
break;
|
||||||
|
case "transfer":
|
||||||
|
if (!deviceId) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: "deviceId is required for transfer action",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await spotifyClient.transferPlayback(deviceId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return new Response(JSON.stringify({ error: "Invalid action" }), {
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({ success: true }), {
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Playback control error:", error);
|
||||||
|
|
||||||
|
if (error instanceof Error) {
|
||||||
|
if (error.message.includes("NO_ACTIVE_DEVICE")) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error:
|
||||||
|
"No active device found. Please select a device first.",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.message.includes("PREMIUM_REQUIRED")) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: "Spotify Premium is required for playback control.",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 403,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error("Full error details:", {
|
||||||
|
message: error.message,
|
||||||
|
stack: error.stack,
|
||||||
|
name: error.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: "Playback control failed",
|
||||||
|
details: error instanceof Error ? error.message : "Unknown error",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
GET: async ({ request }: { request: Request }) => {
|
||||||
|
try {
|
||||||
|
const accessToken = getAccessTokenFromCookies(request);
|
||||||
|
if (!accessToken) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "No access token found" }),
|
||||||
|
{
|
||||||
|
status: 401,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const type = url.searchParams.get("type");
|
||||||
|
|
||||||
|
const spotifyClient = new SpotifyWebApiClient(accessToken);
|
||||||
|
|
||||||
|
if (type === "devices") {
|
||||||
|
const devices = await spotifyClient.getDevices();
|
||||||
|
return new Response(JSON.stringify({ devices }), {
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
} else if (type === "state") {
|
||||||
|
const playbackState = await spotifyClient.getPlaybackState();
|
||||||
|
return new Response(JSON.stringify({ playbackState }), {
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const [devices, playbackState] = await Promise.all([
|
||||||
|
spotifyClient.getDevices(),
|
||||||
|
spotifyClient.getPlaybackState(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({ devices, playbackState }), {
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Get playback data error:", error);
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "Failed to get playback data" }),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
71
src/app/routes/api/spotify/resume.ts
Normal file
71
src/app/routes/api/spotify/resume.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { SpotifyWebApiClient } from "@/lib/spotify/client";
|
||||||
|
import type { SpotifyPlaybackSnapshot } from "@/lib/spotify/types";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/api/spotify/resume")({
|
||||||
|
server: {
|
||||||
|
handlers: {
|
||||||
|
POST: async ({ request }: { request: Request }) => {
|
||||||
|
try {
|
||||||
|
const cookies = request.headers.get("Cookie") || "";
|
||||||
|
const accessTokenMatch = cookies.match(
|
||||||
|
/spotify_access_token=([^;]+)/
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!accessTokenMatch) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "No access token found" }),
|
||||||
|
{
|
||||||
|
status: 401,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessToken = decodeURIComponent(accessTokenMatch[1]);
|
||||||
|
const spotifyClient = new SpotifyWebApiClient(accessToken);
|
||||||
|
|
||||||
|
const body = await request.json();
|
||||||
|
const { snapshot } = body as { snapshot: SpotifyPlaybackSnapshot };
|
||||||
|
|
||||||
|
if (!snapshot) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "No snapshot provided" }),
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await spotifyClient.restorePlaybackSnapshot(snapshot);
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({ success: true }), {
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Spotify resume error:", error);
|
||||||
|
|
||||||
|
let errorMessage = "Failed to resume playback state";
|
||||||
|
|
||||||
|
if (error instanceof Error) {
|
||||||
|
if (
|
||||||
|
error.message.includes("Premium") ||
|
||||||
|
error.message.includes("403")
|
||||||
|
) {
|
||||||
|
errorMessage = "Spotify premium required";
|
||||||
|
} else {
|
||||||
|
errorMessage = error.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({ error: errorMessage }), {
|
||||||
|
status: 500,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
87
src/app/routes/api/spotify/search.ts
Normal file
87
src/app/routes/api/spotify/search.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
async function getClientCredentialsToken(): Promise<string> {
|
||||||
|
const clientId = process.env.VITE_SPOTIFY_CLIENT_ID;
|
||||||
|
const clientSecret = process.env.SPOTIFY_CLIENT_SECRET;
|
||||||
|
|
||||||
|
if (!clientId || !clientSecret) {
|
||||||
|
throw new Error("Missing Spotify client credentials");
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch("https://accounts.spotify.com/api/token", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
Authorization: `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString("base64")}`,
|
||||||
|
},
|
||||||
|
body: "grant_type=client_credentials",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to get Spotify access token");
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data.access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/api/spotify/search")({
|
||||||
|
server: {
|
||||||
|
handlers: {
|
||||||
|
GET: async ({ request }: { request: Request }) => {
|
||||||
|
try {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const query = url.searchParams.get("q");
|
||||||
|
|
||||||
|
if (!query) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "Query parameter q is required" }),
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get client credentials access token
|
||||||
|
const accessToken = await getClientCredentialsToken();
|
||||||
|
|
||||||
|
// Search using Spotify API directly
|
||||||
|
const searchUrl = `https://api.spotify.com/v1/search?q=${encodeURIComponent(query)}&type=track&limit=20`;
|
||||||
|
|
||||||
|
const searchResponse = await fetch(searchUrl, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!searchResponse.ok) {
|
||||||
|
throw new Error("Spotify search request failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchResult = await searchResponse.json();
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ tracks: searchResult.tracks.items }),
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Search error:", error);
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: "Search failed",
|
||||||
|
details: error instanceof Error ? error.message : "Unknown error",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
131
src/app/routes/api/spotify/token.ts
Normal file
131
src/app/routes/api/spotify/token.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
const SPOTIFY_CLIENT_ID = process.env.VITE_SPOTIFY_CLIENT_ID!;
|
||||||
|
const SPOTIFY_CLIENT_SECRET = process.env.SPOTIFY_CLIENT_SECRET!;
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/api/spotify/token")({
|
||||||
|
server: {
|
||||||
|
handlers: {
|
||||||
|
POST: async ({ request }: { request: Request }) => {
|
||||||
|
try {
|
||||||
|
const body = await request.json();
|
||||||
|
const { refresh_token } = body;
|
||||||
|
|
||||||
|
if (!refresh_token) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "refresh_token is required" }),
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenResponse = await fetch(
|
||||||
|
"https://accounts.spotify.com/api/token",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
Authorization: `Basic ${Buffer.from(`${SPOTIFY_CLIENT_ID}:${SPOTIFY_CLIENT_SECRET}`).toString("base64")}`,
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({
|
||||||
|
grant_type: "refresh_token",
|
||||||
|
refresh_token,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!tokenResponse.ok) {
|
||||||
|
const error = await tokenResponse.json();
|
||||||
|
console.error("Token refresh error:", error);
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: "Failed to refresh token",
|
||||||
|
details: error,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: tokenResponse.status,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokens = await tokenResponse.json();
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
access_token: tokens.access_token,
|
||||||
|
expires_in: tokens.expires_in,
|
||||||
|
scope: tokens.scope,
|
||||||
|
token_type: tokens.token_type,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Token refresh endpoint error:", error);
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "Internal server error" }),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
GET: async ({ request }: { request: Request }) => {
|
||||||
|
try {
|
||||||
|
const cookieHeader = request.headers.get("cookie");
|
||||||
|
if (!cookieHeader) {
|
||||||
|
return new Response(JSON.stringify({ error: "No cookies found" }), {
|
||||||
|
status: 401,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const cookies = Object.fromEntries(
|
||||||
|
cookieHeader.split("; ").map((c: string) => c.split("="))
|
||||||
|
);
|
||||||
|
|
||||||
|
const accessToken = cookies.spotify_access_token;
|
||||||
|
const refreshToken = cookies.spotify_refresh_token;
|
||||||
|
|
||||||
|
if (!accessToken && !refreshToken) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "No Spotify tokens found" }),
|
||||||
|
{
|
||||||
|
status: 401,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
access_token: accessToken || null,
|
||||||
|
refresh_token: refreshToken || null,
|
||||||
|
has_tokens: !!(accessToken || refreshToken),
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Get tokens endpoint error:", error);
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "Internal server error" }),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
148
src/app/routes/api/teams/upload-logo.ts
Normal file
148
src/app/routes/api/teams/upload-logo.ts
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { superTokensRequestMiddleware } from "@/utils/supertokens";
|
||||||
|
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||||
|
import { logger } from "@/lib/logger";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const uploadSchema = z.object({
|
||||||
|
teamId: z.string().min(1, "Team ID is required"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/api/teams/upload-logo")({
|
||||||
|
server: {
|
||||||
|
middleware: [superTokensRequestMiddleware],
|
||||||
|
handlers: {
|
||||||
|
POST: async ({ request, context }) => {
|
||||||
|
try {
|
||||||
|
const userId = context.userAuthId;
|
||||||
|
const isAdmin = context.roles.includes("Admin");
|
||||||
|
|
||||||
|
if (!userId) return new Response("Unauthenticated", { status: 401 });
|
||||||
|
|
||||||
|
const formData = await request.formData();
|
||||||
|
const teamId = formData.get("teamId") as string;
|
||||||
|
const logoFile = formData.get("logo") as File;
|
||||||
|
|
||||||
|
const validationResult = uploadSchema.safeParse({ teamId });
|
||||||
|
if (!validationResult.success) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: "Invalid input",
|
||||||
|
details: validationResult.error.issues,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!logoFile || logoFile.size === 0) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: "Logo file is required",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const allowedTypes = [
|
||||||
|
"image/jpeg",
|
||||||
|
"image/jpg",
|
||||||
|
"image/png",
|
||||||
|
"image/gif",
|
||||||
|
];
|
||||||
|
if (!allowedTypes.includes(logoFile.type)) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: "Invalid file type. Only JPEG, PNG and GIF are allowed.",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxSize = 10 * 1024 * 1024;
|
||||||
|
if (logoFile.size > maxSize) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: "File too large. Maximum size is 10MB.",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const team = await pbAdmin.getTeam(teamId);
|
||||||
|
if (!team) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: "Team not found",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 404,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await pbAdmin.getPlayerByAuthId(userId);
|
||||||
|
if (!team.players.map((p) => p.id).includes(user?.id!) && !isAdmin)
|
||||||
|
return new Response("Unauthorized", { status: 403 });
|
||||||
|
|
||||||
|
logger.info("Uploading team logo", {
|
||||||
|
teamId,
|
||||||
|
fileName: logoFile.name,
|
||||||
|
fileSize: logoFile.size,
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const pbFormData = new FormData();
|
||||||
|
pbFormData.append("logo", logoFile);
|
||||||
|
|
||||||
|
const updatedTeam = await pbAdmin.updateTeam(
|
||||||
|
teamId,
|
||||||
|
pbFormData as any
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.info("Team logo uploaded successfully", {
|
||||||
|
teamId,
|
||||||
|
logo: updatedTeam.logo,
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
team: updatedTeam,
|
||||||
|
message: "Logo uploaded successfully",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error: any) {
|
||||||
|
logger.error("Error uploading team logo:", error);
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: "Failed to upload logo",
|
||||||
|
message: error.message || "Unknown error occurred",
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,115 +1,145 @@
|
|||||||
import { createServerFileRoute } from '@tanstack/react-start/server';
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { superTokensRequestMiddleware } from '@/utils/supertokens';
|
import { superTokensRequestMiddleware } from "@/utils/supertokens";
|
||||||
import { pbAdmin } from '@/lib/pocketbase/client';
|
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||||
import { logger } from '@/lib/logger';
|
import { logger } from "@/lib/logger";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
|
|
||||||
const uploadSchema = z.object({
|
const uploadSchema = z.object({
|
||||||
tournamentId: z.string().min(1, 'Tournament ID is required'),
|
tournamentId: z.string().min(1, "Tournament ID is required"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ServerRoute = createServerFileRoute('/api/tournaments/upload-logo')
|
export const Route = createFileRoute("/api/tournaments/upload-logo")({
|
||||||
.middleware([superTokensRequestMiddleware])
|
server: {
|
||||||
.methods({
|
middleware: [superTokensRequestMiddleware],
|
||||||
|
handlers: {
|
||||||
POST: async ({ request, context }) => {
|
POST: async ({ request, context }) => {
|
||||||
try {
|
try {
|
||||||
const userId = context.userAuthId;
|
const userId = context.userAuthId;
|
||||||
const isAdmin = context.roles.includes("Admin");
|
const isAdmin = context.roles.includes("Admin");
|
||||||
|
|
||||||
if (!userId) return new Response('Unauthenticated', { status: 401 });
|
if (!userId) return new Response("Unauthenticated", { status: 401 });
|
||||||
if (!isAdmin) return new Response('Unauthorized', { status: 403 });
|
if (!isAdmin) return new Response("Unauthorized", { status: 403 });
|
||||||
|
|
||||||
const formData = await request.formData();
|
const formData = await request.formData();
|
||||||
const tournamentId = formData.get('tournamentId') as string;
|
const tournamentId = formData.get("tournamentId") as string;
|
||||||
const logoFile = formData.get('logo') as File;
|
const logoFile = formData.get("logo") as File;
|
||||||
|
|
||||||
const validationResult = uploadSchema.safeParse({ tournamentId });
|
const validationResult = uploadSchema.safeParse({ tournamentId });
|
||||||
if (!validationResult.success) {
|
if (!validationResult.success) {
|
||||||
return new Response(JSON.stringify({
|
return new Response(
|
||||||
error: 'Invalid input',
|
JSON.stringify({
|
||||||
details: validationResult.error.issues
|
error: "Invalid input",
|
||||||
}), {
|
details: validationResult.error.issues,
|
||||||
|
}),
|
||||||
|
{
|
||||||
status: 400,
|
status: 400,
|
||||||
headers: { 'Content-Type': 'application/json' }
|
headers: { "Content-Type": "application/json" },
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!logoFile || logoFile.size === 0) {
|
if (!logoFile || logoFile.size === 0) {
|
||||||
return new Response(JSON.stringify({
|
return new Response(
|
||||||
error: 'Logo file is required'
|
JSON.stringify({
|
||||||
}), {
|
error: "Logo file is required",
|
||||||
|
}),
|
||||||
|
{
|
||||||
status: 400,
|
status: 400,
|
||||||
headers: { 'Content-Type': 'application/json' }
|
headers: { "Content-Type": "application/json" },
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
|
const allowedTypes = [
|
||||||
|
"image/jpeg",
|
||||||
|
"image/jpg",
|
||||||
|
"image/png",
|
||||||
|
"image/gif",
|
||||||
|
];
|
||||||
if (!allowedTypes.includes(logoFile.type)) {
|
if (!allowedTypes.includes(logoFile.type)) {
|
||||||
return new Response(JSON.stringify({
|
return new Response(
|
||||||
error: 'Invalid file type. Only JPEG, PNG and GIF are allowed.'
|
JSON.stringify({
|
||||||
}), {
|
error: "Invalid file type. Only JPEG, PNG and GIF are allowed.",
|
||||||
|
}),
|
||||||
|
{
|
||||||
status: 400,
|
status: 400,
|
||||||
headers: { 'Content-Type': 'application/json' }
|
headers: { "Content-Type": "application/json" },
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxSize = 10 * 1024 * 1024;
|
const maxSize = 10 * 1024 * 1024;
|
||||||
if (logoFile.size > maxSize) {
|
if (logoFile.size > maxSize) {
|
||||||
return new Response(JSON.stringify({
|
return new Response(
|
||||||
error: 'File too large. Maximum size is 10MB.'
|
JSON.stringify({
|
||||||
}), {
|
error: "File too large. Maximum size is 10MB.",
|
||||||
|
}),
|
||||||
|
{
|
||||||
status: 400,
|
status: 400,
|
||||||
headers: { 'Content-Type': 'application/json' }
|
headers: { "Content-Type": "application/json" },
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tournament = await pbAdmin.getTournament(tournamentId);
|
const tournament = await pbAdmin.getTournament(tournamentId);
|
||||||
if (!tournament) {
|
if (!tournament) {
|
||||||
return new Response(JSON.stringify({
|
return new Response(
|
||||||
error: 'Tournament not found'
|
JSON.stringify({
|
||||||
}), {
|
error: "Tournament not found",
|
||||||
|
}),
|
||||||
|
{
|
||||||
status: 404,
|
status: 404,
|
||||||
headers: { 'Content-Type': 'application/json' }
|
headers: { "Content-Type": "application/json" },
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.info("Uploading tournament logo", {
|
||||||
logger.info('Uploading tournament logo', {
|
|
||||||
tournamentId,
|
tournamentId,
|
||||||
fileName: logoFile.name,
|
fileName: logoFile.name,
|
||||||
fileSize: logoFile.size,
|
fileSize: logoFile.size,
|
||||||
userId
|
userId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const pbFormData = new FormData();
|
const pbFormData = new FormData();
|
||||||
pbFormData.append('logo', logoFile);
|
pbFormData.append("logo", logoFile);
|
||||||
|
|
||||||
const updatedTournament = await pbAdmin.updateTournament(tournamentId, pbFormData as any);
|
const updatedTournament = await pbAdmin.updateTournament(
|
||||||
|
|
||||||
logger.info('Tournament logo uploaded successfully', {
|
|
||||||
tournamentId,
|
tournamentId,
|
||||||
logo: updatedTournament.logo
|
pbFormData as any
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.info("Tournament logo uploaded successfully", {
|
||||||
|
tournamentId,
|
||||||
|
logo: updatedTournament.logo,
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Response(JSON.stringify({
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
success: true,
|
success: true,
|
||||||
tournament: updatedTournament,
|
tournament: updatedTournament,
|
||||||
message: 'Logo uploaded successfully'
|
message: "Logo uploaded successfully",
|
||||||
}), {
|
}),
|
||||||
|
{
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: { 'Content-Type': 'application/json' }
|
headers: { "Content-Type": "application/json" },
|
||||||
});
|
}
|
||||||
|
);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
logger.error('Error uploading tournament logo:', error);
|
logger.error("Error uploading tournament logo:", error);
|
||||||
|
|
||||||
return new Response(JSON.stringify({
|
return new Response(
|
||||||
error: 'Failed to upload logo',
|
JSON.stringify({
|
||||||
message: error.message || 'Unknown error occurred'
|
error: "Failed to upload logo",
|
||||||
}), {
|
message: error.message || "Unknown error occurred",
|
||||||
|
}),
|
||||||
|
{
|
||||||
status: 500,
|
status: 500,
|
||||||
headers: { 'Content-Type': 'application/json' }
|
headers: { "Content-Type": "application/json" },
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,27 +1,33 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
import { useEffect } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
import FullScreenLoader from '@/components/full-screen-loader'
|
import FullScreenLoader from '@/components/full-screen-loader'
|
||||||
import { attemptRefreshingSession } from 'supertokens-web-js/recipe/session'
|
import { attemptRefreshingSession } from 'supertokens-web-js/recipe/session'
|
||||||
|
import { resetRefreshFlag } from '@/lib/supertokens/client'
|
||||||
|
|
||||||
export const Route = createFileRoute('/refresh-session')({
|
export const Route = createFileRoute('/refresh-session')({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
})
|
})
|
||||||
|
|
||||||
// https://supertokens.com/docs/additional-verification/session-verification/ssr?uiType=custom
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
|
const hasAttemptedRef = useRef(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (hasAttemptedRef.current) return;
|
||||||
|
hasAttemptedRef.current = true;
|
||||||
|
|
||||||
const handleRefresh = async () => {
|
const handleRefresh = async () => {
|
||||||
try {
|
try {
|
||||||
|
resetRefreshFlag();
|
||||||
const refreshed = await attemptRefreshingSession()
|
const refreshed = await attemptRefreshingSession()
|
||||||
|
|
||||||
if (refreshed) {
|
if (refreshed) {
|
||||||
const urlParams = new URLSearchParams(window.location.search)
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
const redirect = urlParams.get('redirect')
|
const redirect = urlParams.get('redirect')
|
||||||
|
|
||||||
if (redirect) {
|
if (redirect && !redirect.includes('_serverFn') && !redirect.includes('/api/')) {
|
||||||
window.location.href = decodeURIComponent(redirect)
|
window.location.href = decodeURIComponent(redirect)
|
||||||
} else {
|
} else {
|
||||||
window.location.href = '/'
|
window.location.href = '/';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
window.location.href = '/login'
|
window.location.href = '/login'
|
||||||
@@ -31,8 +37,7 @@ function RouteComponent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeout = setTimeout(handleRefresh, 100)
|
setTimeout(handleRefresh, 100)
|
||||||
return () => clearTimeout(timeout)
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return <FullScreenLoader />
|
return <FullScreenLoader />
|
||||||
|
|||||||
@@ -11,19 +11,18 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Button as MantineButton,
|
Button as MantineButton,
|
||||||
Text,
|
Text,
|
||||||
Title,
|
|
||||||
Stack,
|
Stack,
|
||||||
Group,
|
Group,
|
||||||
Alert,
|
|
||||||
Collapse,
|
Collapse,
|
||||||
Code,
|
Code,
|
||||||
ThemeIcon
|
Container,
|
||||||
|
Center
|
||||||
} from '@mantine/core'
|
} from '@mantine/core'
|
||||||
import { useDisclosure } from '@mantine/hooks'
|
import { useDisclosure } from '@mantine/hooks'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import toast from '@/lib/sonner'
|
import toast from '@/lib/sonner'
|
||||||
import { logger } from '@/lib/logger'
|
import { logger } from '@/lib/logger'
|
||||||
import { ExclamationMarkIcon, XCircleIcon } from '@phosphor-icons/react'
|
import { XCircleIcon, WarningIcon } from '@phosphor-icons/react'
|
||||||
import Button from './button'
|
import Button from './button'
|
||||||
|
|
||||||
export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
|
export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
|
||||||
@@ -50,25 +49,15 @@ export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
|
|||||||
|
|
||||||
if (errorMessage.toLowerCase().includes('unauthorized')) {
|
if (errorMessage.toLowerCase().includes('unauthorized')) {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Container size="sm" py="xl">
|
||||||
style={{
|
<Center>
|
||||||
display: 'flex',
|
<Stack align="center" gap="md">
|
||||||
flexDirection: 'column',
|
<XCircleIcon size={64} color="var(--mantine-color-red-6)" />
|
||||||
alignItems: 'center',
|
<Text size="xl" fw={600}>Access Denied</Text>
|
||||||
justifyContent: 'center',
|
<Text c="dimmed" ta="center">
|
||||||
minHeight: '50vh',
|
You don't have permission to access this page.
|
||||||
padding: 'var(--mantine-spacing-xl)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Stack align="center" gap="lg">
|
|
||||||
<ThemeIcon color="red" size={80} radius="xl">
|
|
||||||
<XCircleIcon size={48} />
|
|
||||||
</ThemeIcon>
|
|
||||||
<Title order={2} ta="center">Access Denied</Title>
|
|
||||||
<Text size="lg" c="dimmed" ta="center">
|
|
||||||
You don't have permission to access this.
|
|
||||||
</Text>
|
</Text>
|
||||||
<Group>
|
<Group gap="sm" mt="md">
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
onClick={() => window.history.back()}
|
onClick={() => window.history.back()}
|
||||||
@@ -84,59 +73,46 @@ export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
|
|||||||
</MantineButton>
|
</MantineButton>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Center>
|
||||||
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Container size="sm" py="xl">
|
||||||
style={{
|
<Center>
|
||||||
display: 'flex',
|
<Stack align="center" gap="md" w="100%">
|
||||||
flexDirection: 'column',
|
<WarningIcon size={64} color="var(--mantine-color-red-6)" />
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
minHeight: '50vh',
|
|
||||||
padding: 'var(--mantine-spacing-xl)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Stack align="center" gap="lg" maw={600}>
|
|
||||||
<ThemeIcon color="red" size={80} radius="xl">
|
|
||||||
<ExclamationMarkIcon size={48} />
|
|
||||||
</ThemeIcon>
|
|
||||||
|
|
||||||
<Title order={2} ta="center">Something went wrong</Title>
|
<Text size="xl" fw={600}>Something went wrong</Text>
|
||||||
|
|
||||||
<Text size="lg" c="dimmed" ta="center">
|
<Text c="dimmed" ta="center">
|
||||||
There was an unexpected error. Please try again later.
|
An error occurred while loading this page.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Alert
|
<Box w="100%" mt="md">
|
||||||
variant="light"
|
<Text size="sm" c="dimmed" mb="xs">Error: {errorMessage}</Text>
|
||||||
color="red"
|
|
||||||
title="Error Details"
|
|
||||||
w="100%"
|
|
||||||
>
|
|
||||||
<Text mb="sm">{errorMessage}</Text>
|
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
size="compact-sm"
|
size="compact-sm"
|
||||||
onClick={toggleDetails}
|
onClick={toggleDetails}
|
||||||
|
fullWidth
|
||||||
>
|
>
|
||||||
{detailsOpened ? 'Hide' : 'Show'} stack trace
|
{detailsOpened ? 'Hide' : 'Show'} details
|
||||||
</Button>
|
</Button>
|
||||||
<Collapse in={detailsOpened}>
|
<Collapse in={detailsOpened}>
|
||||||
<Code block mt="md" p="md">
|
<Code block mt="sm" p="sm" style={{ fontSize: '11px' }}>
|
||||||
{errorStack}
|
{errorStack}
|
||||||
</Code>
|
</Code>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</Alert>
|
</Box>
|
||||||
|
|
||||||
<Group>
|
<Group gap="sm" mt="lg">
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
onClick={() => router.invalidate()}
|
onClick={() => router.invalidate()}
|
||||||
>
|
>
|
||||||
Try Again
|
Retry
|
||||||
</Button>
|
</Button>
|
||||||
{isRoot ? (
|
{isRoot ? (
|
||||||
<MantineButton
|
<MantineButton
|
||||||
@@ -156,6 +132,7 @@ export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
|
|||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Center>
|
||||||
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,14 @@ import {
|
|||||||
Avatar as MantineAvatar,
|
Avatar as MantineAvatar,
|
||||||
AvatarProps as MantineAvatarProps,
|
AvatarProps as MantineAvatarProps,
|
||||||
Paper,
|
Paper,
|
||||||
|
Modal,
|
||||||
|
Image,
|
||||||
|
Group,
|
||||||
|
Text,
|
||||||
|
ActionIcon,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { XIcon } from "@phosphor-icons/react";
|
||||||
|
|
||||||
interface AvatarProps
|
interface AvatarProps
|
||||||
extends Omit<MantineAvatarProps, "radius" | "color" | "size"> {
|
extends Omit<MantineAvatarProps, "radius" | "color" | "size"> {
|
||||||
@@ -10,6 +17,8 @@ interface AvatarProps
|
|||||||
size?: number;
|
size?: number;
|
||||||
radius?: string | number;
|
radius?: string | number;
|
||||||
withBorder?: boolean;
|
withBorder?: boolean;
|
||||||
|
disableFullscreen?: boolean;
|
||||||
|
contain?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Avatar = ({
|
const Avatar = ({
|
||||||
@@ -17,10 +26,39 @@ const Avatar = ({
|
|||||||
size = 35,
|
size = 35,
|
||||||
radius = "100%",
|
radius = "100%",
|
||||||
withBorder = true,
|
withBorder = true,
|
||||||
|
disableFullscreen = false,
|
||||||
|
contain = false,
|
||||||
...props
|
...props
|
||||||
}: AvatarProps) => {
|
}: AvatarProps) => {
|
||||||
|
const [isFullscreenOpen, setIsFullscreenOpen] = useState(false);
|
||||||
|
const hasImage = Boolean(props.src);
|
||||||
|
|
||||||
|
const handleAvatarClick = () => {
|
||||||
|
if (hasImage && !disableFullscreen) {
|
||||||
|
setIsFullscreenOpen(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper p={size / 20} radius={radius} withBorder={withBorder}>
|
<>
|
||||||
|
<Paper
|
||||||
|
p={size / 20}
|
||||||
|
radius={radius}
|
||||||
|
withBorder={withBorder}
|
||||||
|
style={{
|
||||||
|
cursor: hasImage && !disableFullscreen ? 'pointer' : 'default',
|
||||||
|
transition: 'transform 0.15s ease',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
if (hasImage && !disableFullscreen) {
|
||||||
|
e.currentTarget.style.transform = 'scale(1.02)';
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.transform = 'scale(1)';
|
||||||
|
}}
|
||||||
|
onClick={handleAvatarClick}
|
||||||
|
>
|
||||||
<MantineAvatar
|
<MantineAvatar
|
||||||
alt={name}
|
alt={name}
|
||||||
key={name}
|
key={name}
|
||||||
@@ -28,15 +66,82 @@ const Avatar = ({
|
|||||||
color="initials"
|
color="initials"
|
||||||
size={size}
|
size={size}
|
||||||
radius={radius}
|
radius={radius}
|
||||||
w="fit-content"
|
w={size}
|
||||||
styles={{
|
styles={{
|
||||||
image: {
|
image: {
|
||||||
objectFit: "contain",
|
objectFit: contain ? 'contain' : 'cover',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
opened={isFullscreenOpen}
|
||||||
|
onClose={() => setIsFullscreenOpen(false)}
|
||||||
|
size="auto"
|
||||||
|
centered
|
||||||
|
withCloseButton={false}
|
||||||
|
overlayProps={{
|
||||||
|
backgroundOpacity: 0.9,
|
||||||
|
blur: 2,
|
||||||
|
}}
|
||||||
|
styles={{
|
||||||
|
content: {
|
||||||
|
background: 'transparent',
|
||||||
|
border: 'none',
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ position: 'relative', maxWidth: '90vw', maxHeight: '90vh' }}>
|
||||||
|
<ActionIcon
|
||||||
|
variant="filled"
|
||||||
|
color="dark"
|
||||||
|
size="lg"
|
||||||
|
radius="xl"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: -10,
|
||||||
|
right: -10,
|
||||||
|
zIndex: 1000,
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
||||||
|
}}
|
||||||
|
onClick={() => setIsFullscreenOpen(false)}
|
||||||
|
>
|
||||||
|
<XIcon size={18} color="white" />
|
||||||
|
</ActionIcon>
|
||||||
|
|
||||||
|
<Image
|
||||||
|
src={props.src}
|
||||||
|
alt={name}
|
||||||
|
fit="contain"
|
||||||
|
style={{
|
||||||
|
borderRadius: 8,
|
||||||
|
maxWidth: '90vw',
|
||||||
|
maxHeight: '90vh',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Group
|
||||||
|
justify="center"
|
||||||
|
mt="md"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: -50,
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text c="white" size="sm" fw={500}>
|
||||||
|
{name}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
57
src/components/countdown.tsx
Normal file
57
src/components/countdown.tsx
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import useNow from '@/hooks/use-now';
|
||||||
|
import { Text, Group } from '@mantine/core';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
interface CountdownProps {
|
||||||
|
date: Date;
|
||||||
|
label?: string;
|
||||||
|
color?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TimeLeft {
|
||||||
|
days: number;
|
||||||
|
hours: number;
|
||||||
|
minutes: number;
|
||||||
|
seconds: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateTimeLeft(targetDate: Date, currentTime = new Date()): TimeLeft {
|
||||||
|
const difference = targetDate.getTime() - currentTime.getTime();
|
||||||
|
|
||||||
|
if (difference <= 0) {
|
||||||
|
return { days: 0, hours: 0, minutes: 0, seconds: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
days: Math.floor(difference / (1000 * 60 * 60 * 24)),
|
||||||
|
hours: Math.floor((difference / (1000 * 60 * 60)) % 24),
|
||||||
|
minutes: Math.floor((difference / 1000 / 60) % 60),
|
||||||
|
seconds: Math.floor((difference / 1000) % 60)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Countdown({ date, label, color }: CountdownProps) {
|
||||||
|
const now = useNow();
|
||||||
|
const timeLeft = useMemo(() => calculateTimeLeft(date, now), [date, now]);
|
||||||
|
|
||||||
|
const formatTime = () => {
|
||||||
|
const pad = (num: number) => num.toString().padStart(2, '0');
|
||||||
|
|
||||||
|
if (timeLeft.days > 0) {
|
||||||
|
return `${timeLeft.days}d ${pad(timeLeft.hours)}:${pad(timeLeft.minutes)}:${pad(timeLeft.seconds)}`;
|
||||||
|
} else {
|
||||||
|
return `${pad(timeLeft.hours)}:${pad(timeLeft.minutes)}:${pad(timeLeft.seconds)}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group gap="xs">
|
||||||
|
{label && <Text size='sm' fw={500}>{label}:</Text>}
|
||||||
|
<Text size='sm' fw={600} c={color} ff="monospace">
|
||||||
|
{formatTime()}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Countdown;
|
||||||
@@ -6,14 +6,16 @@ interface ListLinkProps {
|
|||||||
label: string;
|
label: string;
|
||||||
to: string;
|
to: string;
|
||||||
Icon?: Icon;
|
Icon?: Icon;
|
||||||
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const ListLink = ({ label, to, Icon }: ListLinkProps) => {
|
const ListLink = ({ label, to, Icon, disabled=false }: ListLinkProps) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NavLink
|
<NavLink
|
||||||
|
disabled={disabled}
|
||||||
w="100%"
|
w="100%"
|
||||||
p="md"
|
p="md"
|
||||||
component={"button"}
|
component={"button"}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Container, ContainerProps } from "@mantine/core";
|
import { Container, ContainerProps, Box } from "@mantine/core";
|
||||||
import useRouterConfig from "@/features/core/hooks/use-router-config";
|
import useRouterConfig from "@/features/core/hooks/use-router-config";
|
||||||
|
import BackButton from "@/features/core/components/back-button";
|
||||||
|
import SettingsButton from "@/features/core/components/settings-button";
|
||||||
|
|
||||||
interface PageProps extends ContainerProps, React.PropsWithChildren {
|
interface PageProps extends ContainerProps, React.PropsWithChildren {
|
||||||
noPadding?: boolean;
|
noPadding?: boolean;
|
||||||
@@ -16,8 +18,15 @@ const Page = ({ children, noPadding, fullWidth, ...props }: PageProps) => {
|
|||||||
m={0}
|
m={0}
|
||||||
maw={fullWidth ? '100%' : 600}
|
maw={fullWidth ? '100%' : 600}
|
||||||
mx="auto"
|
mx="auto"
|
||||||
|
pos="relative"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
{header.collapsed && header.withBackButton && (
|
||||||
|
<BackButton top={4} />
|
||||||
|
)}
|
||||||
|
{header.collapsed && header.settingsLink && (
|
||||||
|
<SettingsButton to={header.settingsLink} />
|
||||||
|
)}
|
||||||
{children}
|
{children}
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|||||||
41
src/components/rich-text-editor.tsx
Normal file
41
src/components/rich-text-editor.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { useEditor } from '@tiptap/react';
|
||||||
|
import StarterKit from '@tiptap/starter-kit';
|
||||||
|
import { RichTextEditor as MantineRichTextEditor } from '@mantine/tiptap';
|
||||||
|
|
||||||
|
interface RichTextEditorProps {
|
||||||
|
value: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RichTextEditor({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: RichTextEditorProps) {
|
||||||
|
const editor = useEditor({
|
||||||
|
extensions: [StarterKit],
|
||||||
|
content: value,
|
||||||
|
immediatelyRender: false,
|
||||||
|
onUpdate: ({ editor }) => {
|
||||||
|
onChange(editor.getHTML());
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MantineRichTextEditor editor={editor}>
|
||||||
|
<MantineRichTextEditor.Toolbar>
|
||||||
|
<MantineRichTextEditor.ControlsGroup>
|
||||||
|
<MantineRichTextEditor.Bold />
|
||||||
|
<MantineRichTextEditor.Italic />
|
||||||
|
</MantineRichTextEditor.ControlsGroup>
|
||||||
|
<MantineRichTextEditor.ControlsGroup>
|
||||||
|
<MantineRichTextEditor.Blockquote />
|
||||||
|
<MantineRichTextEditor.Hr />
|
||||||
|
<MantineRichTextEditor.BulletList />
|
||||||
|
<MantineRichTextEditor.OrderedList />
|
||||||
|
</MantineRichTextEditor.ControlsGroup>
|
||||||
|
</MantineRichTextEditor.Toolbar>
|
||||||
|
|
||||||
|
<MantineRichTextEditor.Content h="45vh" />
|
||||||
|
</MantineRichTextEditor>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Box, Container, useComputedColorScheme } from "@mantine/core";
|
import { Box, Container, Flex, Loader, useComputedColorScheme } from "@mantine/core";
|
||||||
import { PropsWithChildren, useEffect } from "react";
|
import { PropsWithChildren, Suspense, useEffect, useRef } from "react";
|
||||||
import { Drawer as VaulDrawer } from "vaul";
|
import { Drawer as VaulDrawer } from "vaul";
|
||||||
import { useMantineColorScheme } from "@mantine/core";
|
|
||||||
import styles from "./styles.module.css";
|
import styles from "./styles.module.css";
|
||||||
|
|
||||||
interface DrawerProps extends PropsWithChildren {
|
interface DrawerProps extends PropsWithChildren {
|
||||||
@@ -17,6 +16,7 @@ const Drawer: React.FC<DrawerProps> = ({
|
|||||||
onChange,
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
const colorScheme = useComputedColorScheme("light");
|
const colorScheme = useComputedColorScheme("light");
|
||||||
|
const contentRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const appElement = document.querySelector(".app") as HTMLElement;
|
const appElement = document.querySelector(".app") as HTMLElement;
|
||||||
@@ -59,11 +59,56 @@ const Drawer: React.FC<DrawerProps> = ({
|
|||||||
};
|
};
|
||||||
}, [opened, colorScheme]);
|
}, [opened, colorScheme]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!opened || !contentRef.current) return;
|
||||||
|
|
||||||
|
const updateDrawerHeight = () => {
|
||||||
|
if (contentRef.current) {
|
||||||
|
const drawerContent = contentRef.current;
|
||||||
|
const visualViewport = window.visualViewport;
|
||||||
|
|
||||||
|
if (visualViewport) {
|
||||||
|
const availableHeight = visualViewport.height;
|
||||||
|
const maxDrawerHeight = Math.min(availableHeight * 0.75, window.innerHeight * 0.75);
|
||||||
|
|
||||||
|
drawerContent.style.maxHeight = `${maxDrawerHeight}px`;
|
||||||
|
} else {
|
||||||
|
drawerContent.style.maxHeight = '75vh';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
|
if (contentRef.current) {
|
||||||
|
const drawerContent = contentRef.current.closest('[data-vaul-drawer-wrapper]');
|
||||||
|
if (drawerContent) {
|
||||||
|
(drawerContent as HTMLElement).style.height = 'auto';
|
||||||
|
(drawerContent as HTMLElement).offsetHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateDrawerHeight();
|
||||||
|
|
||||||
|
if (window.visualViewport) {
|
||||||
|
window.visualViewport.addEventListener('resize', updateDrawerHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeObserver.observe(contentRef.current);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
if (window.visualViewport) {
|
||||||
|
window.visualViewport.removeEventListener('resize', updateDrawerHeight);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [opened, children]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VaulDrawer.Root open={opened} onOpenChange={onChange}>
|
<VaulDrawer.Root repositionInputs={false} open={opened} onOpenChange={onChange}>
|
||||||
<VaulDrawer.Portal>
|
<VaulDrawer.Portal>
|
||||||
<VaulDrawer.Overlay className={styles.drawerOverlay} />
|
<VaulDrawer.Overlay className={styles.drawerOverlay} />
|
||||||
<VaulDrawer.Content className={styles.drawerContent}>
|
<VaulDrawer.Content className={styles.drawerContent} aria-describedby="drawer" ref={contentRef}>
|
||||||
<Container flex={1} p="md">
|
<Container flex={1} p="md">
|
||||||
<Box
|
<Box
|
||||||
mb="sm"
|
mb="sm"
|
||||||
@@ -74,9 +119,15 @@ const Drawer: React.FC<DrawerProps> = ({
|
|||||||
mr="auto"
|
mr="auto"
|
||||||
style={{ borderRadius: "9999px" }}
|
style={{ borderRadius: "9999px" }}
|
||||||
/>
|
/>
|
||||||
<Container mah="fit-content" mx="auto" maw="28rem" px={0}>
|
<Container mx="auto" maw="28rem" px={0}>
|
||||||
<VaulDrawer.Title>{title}</VaulDrawer.Title>
|
<VaulDrawer.Title>{title}</VaulDrawer.Title>
|
||||||
|
<Suspense fallback={
|
||||||
|
<Flex justify='center' align='center' w='100%' h={400}>
|
||||||
|
<Loader size='lg' />
|
||||||
|
</Flex>
|
||||||
|
}>
|
||||||
{children}
|
{children}
|
||||||
|
</Suspense>
|
||||||
</Container>
|
</Container>
|
||||||
</Container>
|
</Container>
|
||||||
</VaulDrawer.Content>
|
</VaulDrawer.Content>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Modal as MantineModal, Title } from "@mantine/core";
|
import { Flex, Loader, Modal as MantineModal, Title } from "@mantine/core";
|
||||||
import { PropsWithChildren } from "react";
|
import { PropsWithChildren, Suspense } from "react";
|
||||||
|
|
||||||
interface ModalProps extends PropsWithChildren {
|
interface ModalProps extends PropsWithChildren {
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -12,8 +12,16 @@ const Modal: React.FC<ModalProps> = ({ title, children, opened, onClose }) => (
|
|||||||
opened={opened}
|
opened={opened}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
title={<Title order={3}>{title}</Title>}
|
title={<Title order={3}>{title}</Title>}
|
||||||
|
>
|
||||||
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<Flex justify="center" align="center" w="100%" h={400}>
|
||||||
|
<Loader size="lg" />
|
||||||
|
</Flex>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
</Suspense>
|
||||||
</MantineModal>
|
</MantineModal>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { PropsWithChildren, useCallback } from "react";
|
|||||||
import { useIsMobile } from "@/hooks/use-is-mobile";
|
import { useIsMobile } from "@/hooks/use-is-mobile";
|
||||||
import Drawer from "./drawer";
|
import Drawer from "./drawer";
|
||||||
import Modal from "./modal";
|
import Modal from "./modal";
|
||||||
import { Box, ScrollArea } from "@mantine/core";
|
import { ScrollArea } from "@mantine/core";
|
||||||
|
|
||||||
interface SheetProps extends PropsWithChildren {
|
interface SheetProps extends PropsWithChildren {
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -23,14 +23,14 @@ const Sheet: React.FC<SheetProps> = ({ title, children, opened, onChange }) => {
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
>
|
>
|
||||||
<ScrollArea
|
<ScrollArea.Autosize
|
||||||
style={{ flex: 1 }}
|
style={{ flex: 1, maxHeight: '75dvh' }}
|
||||||
scrollbarSize={8}
|
scrollbarSize={8}
|
||||||
scrollbars="y"
|
scrollbars="y"
|
||||||
type="scroll"
|
type="scroll"
|
||||||
>
|
>
|
||||||
<Box mah="70vh">{children}</Box>
|
{children}
|
||||||
</ScrollArea>
|
</ScrollArea.Autosize>
|
||||||
</SheetComponent>
|
</SheetComponent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Box, Text, UnstyledButton, Flex, Stack } from "@mantine/core";
|
import { Box, Text, UnstyledButton, Flex, Stack } from "@mantine/core";
|
||||||
import { CaretRightIcon } from "@phosphor-icons/react";
|
import { CaretRightIcon } from "@phosphor-icons/react";
|
||||||
import { ComponentType, useContext } from "react";
|
import React, { ComponentType, useContext } from "react";
|
||||||
import { SlidePanelContext } from "./slide-panel-context";
|
import { SlidePanelContext } from "./slide-panel-context";
|
||||||
|
|
||||||
interface SlidePanelFieldProps {
|
interface SlidePanelFieldProps {
|
||||||
@@ -11,7 +11,7 @@ interface SlidePanelFieldProps {
|
|||||||
title: string;
|
title: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
formatValue?: (value: any) => string;
|
formatValue?: (value: any) => string | React.ReactNode;
|
||||||
componentProps?: Record<string, any>;
|
componentProps?: Record<string, any>;
|
||||||
withAsterisk?: boolean;
|
withAsterisk?: boolean;
|
||||||
error?: string;
|
error?: string;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ interface SlidePanelProps {
|
|||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
submitText?: string;
|
submitText?: string;
|
||||||
cancelText?: string;
|
cancelText?: string;
|
||||||
|
cancelColor?: string;
|
||||||
maxHeight?: string;
|
maxHeight?: string;
|
||||||
formProps?: Record<string, any>;
|
formProps?: Record<string, any>;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
@@ -28,6 +29,7 @@ const SlidePanel = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
submitText = "Submit",
|
submitText = "Submit",
|
||||||
cancelText = "Cancel",
|
cancelText = "Cancel",
|
||||||
|
cancelColor = "red",
|
||||||
maxHeight = "70vh",
|
maxHeight = "70vh",
|
||||||
formProps = {},
|
formProps = {},
|
||||||
loading = false,
|
loading = false,
|
||||||
@@ -114,7 +116,7 @@ const SlidePanel = ({
|
|||||||
{onCancel && (
|
{onCancel && (
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
color="red"
|
color={cancelColor}
|
||||||
fullWidth
|
fullWidth
|
||||||
onClick={onCancel}
|
onClick={onCancel}
|
||||||
type="button"
|
type="button"
|
||||||
@@ -165,14 +167,11 @@ const SlidePanel = ({
|
|||||||
bg="var(--mantine-color-dimmed)"
|
bg="var(--mantine-color-dimmed)"
|
||||||
my="xs"
|
my="xs"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Box>
|
|
||||||
<panelConfig.Component
|
<panelConfig.Component
|
||||||
value={tempValue}
|
value={tempValue}
|
||||||
onChange={setTempValue}
|
onChange={setTempValue}
|
||||||
{...(panelConfig.componentProps || {})}
|
{...(panelConfig.componentProps || {})}
|
||||||
/>
|
/>
|
||||||
</Box>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -11,10 +11,12 @@
|
|||||||
border-top-left-radius: 20px;
|
border-top-left-radius: 20px;
|
||||||
border-top-right-radius: 20px;
|
border-top-right-radius: 20px;
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
height: fit-content;
|
height: auto !important;
|
||||||
|
min-height: fit-content;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
transition: height 0.2s ease-out, max-height 0.2s ease-out;
|
||||||
}
|
}
|
||||||
|
|||||||
161
src/components/stats-overview.tsx
Normal file
161
src/components/stats-overview.tsx
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Text,
|
||||||
|
Group,
|
||||||
|
Stack,
|
||||||
|
ThemeIcon,
|
||||||
|
Skeleton,
|
||||||
|
Divider,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import {
|
||||||
|
CrownIcon,
|
||||||
|
XIcon,
|
||||||
|
FireIcon,
|
||||||
|
ShieldIcon,
|
||||||
|
ChartLineUpIcon,
|
||||||
|
ShieldCheckIcon,
|
||||||
|
BoxingGloveIcon,
|
||||||
|
Icon,
|
||||||
|
ArrowUpIcon,
|
||||||
|
ArrowDownIcon,
|
||||||
|
} from "@phosphor-icons/react";
|
||||||
|
import { BaseStats } from "@/types/stats";
|
||||||
|
|
||||||
|
interface StatsOverviewProps {
|
||||||
|
statsData: BaseStats | null;
|
||||||
|
isLoading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StatItem = ({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
suffix = "",
|
||||||
|
Icon,
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
value: number | null;
|
||||||
|
suffix?: string;
|
||||||
|
Icon?: Icon;
|
||||||
|
isLoading?: boolean;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Group justify="space-between" align="center" py="md" px="sm">
|
||||||
|
<Group gap="sm" align="center">
|
||||||
|
{Icon && (
|
||||||
|
<ThemeIcon size="md" variant="light" radius="sm" color="gray">
|
||||||
|
<Icon size={16} />
|
||||||
|
</ThemeIcon>
|
||||||
|
)}
|
||||||
|
<Text size="sm" fw={500}>
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
{value !== null ? (
|
||||||
|
<Text size="sm" fw={700} c="dimmed">
|
||||||
|
{`${value}${suffix}`}
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<Skeleton width={20} height={20} />
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const StatsOverview = ({ statsData, isLoading = false }: StatsOverviewProps) => {
|
||||||
|
if (!statsData && !isLoading) {
|
||||||
|
return (
|
||||||
|
<Box p="sm" h="auto" mih={200}>
|
||||||
|
<Text ta="center" size="sm" fw={600} c="dimmed">
|
||||||
|
No stats available yet
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!statsData) return null;
|
||||||
|
|
||||||
|
const overallStats = {
|
||||||
|
matches: statsData.matches,
|
||||||
|
wins: statsData.wins,
|
||||||
|
losses: statsData.losses,
|
||||||
|
total_cups_made: statsData.total_cups_made,
|
||||||
|
total_cups_against: statsData.total_cups_against,
|
||||||
|
};
|
||||||
|
|
||||||
|
const avgCupsPerMatch =
|
||||||
|
overallStats.matches > 0
|
||||||
|
? parseFloat((overallStats.total_cups_made / overallStats.matches).toFixed(1))
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
const avgCupsAgainstPerMatch =
|
||||||
|
overallStats.matches > 0
|
||||||
|
? parseFloat((overallStats.total_cups_against / overallStats.matches).toFixed(1))
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
const avgMarginOfVictory = statsData.margin_of_victory ? parseFloat(statsData.margin_of_victory.toFixed(1)) : 0;
|
||||||
|
const avgMarginOfLoss = statsData.margin_of_loss ? parseFloat(statsData.margin_of_loss.toFixed(1)) : 0;
|
||||||
|
|
||||||
|
const allStats = [
|
||||||
|
{ label: "Matches Played", value: overallStats.matches, Icon: BoxingGloveIcon },
|
||||||
|
{ label: "Wins", value: overallStats.wins, Icon: CrownIcon },
|
||||||
|
{ label: "Losses", value: overallStats.losses, Icon: XIcon },
|
||||||
|
{ label: "Cups Made", value: overallStats.total_cups_made, Icon: FireIcon },
|
||||||
|
{ label: "Cups Against", value: overallStats.total_cups_against, Icon: ShieldIcon },
|
||||||
|
{ label: "Avg Cups Per Game", value: avgCupsPerMatch > 0 ? avgCupsPerMatch : null, Icon: ChartLineUpIcon },
|
||||||
|
{ label: "Avg Cups Against", value: avgCupsAgainstPerMatch > 0 ? avgCupsAgainstPerMatch : null, Icon: ShieldCheckIcon },
|
||||||
|
{ label: "Avg Win Margin", value: avgMarginOfVictory > 0 ? avgMarginOfVictory : null, Icon: ArrowUpIcon },
|
||||||
|
{ label: "Avg Loss Margin", value: avgMarginOfLoss > 0 ? avgMarginOfLoss : null, Icon: ArrowDownIcon },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Stack gap={0}>
|
||||||
|
{allStats.map((stat, index) => (
|
||||||
|
<Box key={stat.label}>
|
||||||
|
<StatItem
|
||||||
|
label={stat.label}
|
||||||
|
value={stat.value}
|
||||||
|
Icon={stat.Icon}
|
||||||
|
isLoading={isLoading}
|
||||||
|
/>
|
||||||
|
{index < allStats.length - 1 && <Divider />}
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const StatsSkeleton = () => {
|
||||||
|
const skeletonStats = [
|
||||||
|
{ label: "Matches Played", Icon: BoxingGloveIcon },
|
||||||
|
{ label: "Wins", Icon: CrownIcon },
|
||||||
|
{ label: "Losses", Icon: XIcon },
|
||||||
|
{ label: "Cups Made", Icon: FireIcon },
|
||||||
|
{ label: "Cups Against", Icon: ShieldIcon },
|
||||||
|
{ label: "Avg Cups Per Game", Icon: ChartLineUpIcon },
|
||||||
|
{ label: "Avg Cups Against", Icon: ShieldCheckIcon },
|
||||||
|
{ label: "Avg Win Margin", Icon: ArrowUpIcon },
|
||||||
|
{ label: "Avg Loss Margin", Icon: ArrowDownIcon },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Stack gap={0}>
|
||||||
|
{skeletonStats.map((stat, index) => (
|
||||||
|
<Box key={stat.label}>
|
||||||
|
<StatItem
|
||||||
|
label={stat.label}
|
||||||
|
value={null}
|
||||||
|
Icon={stat.Icon}
|
||||||
|
isLoading={true}
|
||||||
|
/>
|
||||||
|
{index < skeletonStats.length - 1 && <Divider />}
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StatsOverview;
|
||||||
@@ -86,7 +86,7 @@ function SwipeableTabs({
|
|||||||
}
|
}
|
||||||
}, [search?.tab]);
|
}, [search?.tab]);
|
||||||
|
|
||||||
useEffect(() => {
|
const updateHeight = useCallback(() => {
|
||||||
const activeSlideRef = slideRefs.current[activeTab];
|
const activeSlideRef = slideRefs.current[activeTab];
|
||||||
if (activeSlideRef) {
|
if (activeSlideRef) {
|
||||||
const height = activeSlideRef.scrollHeight;
|
const height = activeSlideRef.scrollHeight;
|
||||||
@@ -94,6 +94,33 @@ function SwipeableTabs({
|
|||||||
}
|
}
|
||||||
}, [activeTab]);
|
}, [activeTab]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateHeight();
|
||||||
|
}, [activeTab, updateHeight]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timeoutId = setTimeout(updateHeight, 0);
|
||||||
|
return () => clearTimeout(timeoutId);
|
||||||
|
}, [updateHeight]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const activeSlideRef = slideRefs.current[activeTab];
|
||||||
|
if (!activeSlideRef) return;
|
||||||
|
|
||||||
|
let timeoutId: any;
|
||||||
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
timeoutId = setTimeout(updateHeight, 16);
|
||||||
|
});
|
||||||
|
|
||||||
|
resizeObserver.observe(activeSlideRef);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
};
|
||||||
|
}, [activeTab, updateHeight]);
|
||||||
|
|
||||||
const setControlRef = useCallback(
|
const setControlRef = useCallback(
|
||||||
(index: number) => (node: HTMLSpanElement | null) => {
|
(index: number) => (node: HTMLSpanElement | null) => {
|
||||||
controlsRefs.current[index] = node;
|
controlsRefs.current[index] = node;
|
||||||
@@ -109,13 +136,14 @@ function SwipeableTabs({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box style={{ touchAction: "pan-y" }}>
|
||||||
<Box
|
<Box
|
||||||
ref={setRootRef}
|
ref={setRootRef}
|
||||||
pos="sticky"
|
pos="sticky"
|
||||||
top={0}
|
top={0}
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
paddingInline: "var(--mantine-spacing-md)",
|
||||||
marginBottom: "var(--mantine-spacing-md)",
|
marginBottom: "var(--mantine-spacing-md)",
|
||||||
zIndex: 100,
|
zIndex: 100,
|
||||||
backgroundColor: "var(--mantine-color-body)",
|
backgroundColor: "var(--mantine-color-body)",
|
||||||
@@ -137,7 +165,7 @@ function SwipeableTabs({
|
|||||||
onClick={() => changeTab(index)}
|
onClick={() => changeTab(index)}
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
padding: "var(--mantine-spacing-sm) var(--mantine-spacing-md)",
|
padding: "var(--mantine-spacing-sm) var(--mantine-spacing-xs)",
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
color:
|
color:
|
||||||
activeTab === index
|
activeTab === index
|
||||||
@@ -155,7 +183,7 @@ function SwipeableTabs({
|
|||||||
component="span"
|
component="span"
|
||||||
style={{
|
style={{
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
paddingInline: "1rem",
|
paddingInline: "0.5rem",
|
||||||
paddingBottom: "0.25rem",
|
paddingBottom: "0.25rem",
|
||||||
}}
|
}}
|
||||||
ref={setControlRef(index)}
|
ref={setControlRef(index)}
|
||||||
@@ -176,6 +204,7 @@ function SwipeableTabs({
|
|||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
height: carouselHeight === "auto" ? "auto" : `${carouselHeight}px`,
|
height: carouselHeight === "auto" ? "auto" : `${carouselHeight}px`,
|
||||||
transition: "height 300ms ease",
|
transition: "height 300ms ease",
|
||||||
|
touchAction: "pan-y",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tabs.map((tab, index) => (
|
{tabs.map((tab, index) => (
|
||||||
|
|||||||
175
src/components/typeahead.tsx
Normal file
175
src/components/typeahead.tsx
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
import { useState, useRef, useEffect, ReactNode } from "react";
|
||||||
|
import { TextInput, Loader, Paper, Stack, Box, Text } from "@mantine/core";
|
||||||
|
import { useDebouncedCallback } from "@mantine/hooks";
|
||||||
|
|
||||||
|
export interface TypeaheadOption<T = any> {
|
||||||
|
id: string;
|
||||||
|
data: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TypeaheadProps<T> {
|
||||||
|
onSelect: (option: TypeaheadOption<T>) => void;
|
||||||
|
searchFn: (query: string) => Promise<TypeaheadOption<T>[]>;
|
||||||
|
renderOption: (option: TypeaheadOption<T>, isSelected?: boolean) => ReactNode;
|
||||||
|
format?: (option: TypeaheadOption<T>) => string;
|
||||||
|
placeholder?: string;
|
||||||
|
debounceMs?: number;
|
||||||
|
disabled?: boolean;
|
||||||
|
initialValue?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Typeahead = <T,>({
|
||||||
|
onSelect,
|
||||||
|
searchFn,
|
||||||
|
renderOption,
|
||||||
|
format,
|
||||||
|
placeholder = "Search...",
|
||||||
|
debounceMs = 300,
|
||||||
|
disabled = false,
|
||||||
|
initialValue = ""
|
||||||
|
}: TypeaheadProps<T>) => {
|
||||||
|
const [searchQuery, setSearchQuery] = useState(initialValue);
|
||||||
|
const [searchResults, setSearchResults] = useState<TypeaheadOption<T>[]>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [selectedIndex, setSelectedIndex] = useState(-1);
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const debouncedSearch = useDebouncedCallback(async (query: string) => {
|
||||||
|
if (!query.trim()) {
|
||||||
|
setSearchResults([]);
|
||||||
|
setIsOpen(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const results = await searchFn(query);
|
||||||
|
setSearchResults(results);
|
||||||
|
setIsOpen(results.length > 0);
|
||||||
|
setSelectedIndex(-1);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Search failed:', error);
|
||||||
|
setSearchResults([]);
|
||||||
|
setIsOpen(false);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, debounceMs);
|
||||||
|
|
||||||
|
const handleSearchChange = (value: string) => {
|
||||||
|
setSearchQuery(value);
|
||||||
|
debouncedSearch(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOptionSelect = (option: TypeaheadOption<T>) => {
|
||||||
|
onSelect(option);
|
||||||
|
const displayValue = format ? format(option) : String(option.data);
|
||||||
|
setSearchQuery(displayValue);
|
||||||
|
setIsOpen(false);
|
||||||
|
setSelectedIndex(-1);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
|
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
|
||||||
|
setIsOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
|
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||||
|
if (!isOpen || searchResults.length === 0) return;
|
||||||
|
|
||||||
|
switch (e.key) {
|
||||||
|
case 'ArrowDown':
|
||||||
|
e.preventDefault();
|
||||||
|
setSelectedIndex(prev => (prev < searchResults.length - 1 ? prev + 1 : prev));
|
||||||
|
break;
|
||||||
|
case 'ArrowUp':
|
||||||
|
e.preventDefault();
|
||||||
|
setSelectedIndex(prev => (prev > 0 ? prev - 1 : prev));
|
||||||
|
break;
|
||||||
|
case 'Enter':
|
||||||
|
e.preventDefault();
|
||||||
|
if (selectedIndex >= 0 && searchResults[selectedIndex]) {
|
||||||
|
handleOptionSelect(searchResults[selectedIndex]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Escape':
|
||||||
|
setIsOpen(false);
|
||||||
|
setSelectedIndex(-1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box ref={containerRef} pos="relative" w="100%">
|
||||||
|
<TextInput
|
||||||
|
ref={inputRef}
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(event) => handleSearchChange(event.currentTarget.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onFocus={() => {
|
||||||
|
if (searchResults.length > 0) setIsOpen(true);
|
||||||
|
}}
|
||||||
|
placeholder={placeholder}
|
||||||
|
rightSection={isLoading ? <Loader size="xs" /> : null}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{isOpen && (
|
||||||
|
<Paper
|
||||||
|
shadow="md"
|
||||||
|
p={0}
|
||||||
|
bd="1px solid var(--mantine-color-dimmed)"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '100%',
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
zIndex: 9999,
|
||||||
|
maxHeight: '160px',
|
||||||
|
overflowY: 'auto',
|
||||||
|
WebkitOverflowScrolling: 'touch',
|
||||||
|
touchAction: 'pan-y',
|
||||||
|
borderTop: 0,
|
||||||
|
borderTopLeftRadius: 0,
|
||||||
|
borderTopRightRadius: 0
|
||||||
|
}}
|
||||||
|
onTouchMove={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
{searchResults.length > 0 ? (
|
||||||
|
<Stack gap={0}>
|
||||||
|
{searchResults.map((option, index) => (
|
||||||
|
<Box
|
||||||
|
key={option.id}
|
||||||
|
style={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
backgroundColor: selectedIndex === index ? 'var(--mantine-color-gray-1)' : 'transparent',
|
||||||
|
}}
|
||||||
|
onClick={() => handleOptionSelect(option)}
|
||||||
|
onMouseEnter={() => setSelectedIndex(index)}
|
||||||
|
>
|
||||||
|
{renderOption(option, selectedIndex === index)}
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
) : (
|
||||||
|
<Box p="md">
|
||||||
|
<Text size="sm" c="dimmed" ta="center">
|
||||||
|
{searchQuery.trim() ? 'No results found' : 'Start typing to search...'}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Paper>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Typeahead;
|
||||||
@@ -1,21 +1,28 @@
|
|||||||
import { createContext, PropsWithChildren, useCallback, useContext, useMemo } from "react";
|
import {
|
||||||
|
createContext,
|
||||||
|
PropsWithChildren,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useMemo,
|
||||||
|
} from "react";
|
||||||
import { MantineColor, MantineColorScheme } from "@mantine/core";
|
import { MantineColor, MantineColorScheme } from "@mantine/core";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { Player } from "@/features/players/types";
|
import { Player } from "@/features/players/types";
|
||||||
import { playerKeys, playerQueries, useMe } from "@/features/players/queries";
|
import { playerKeys, playerQueries, useMe } from "@/features/players/queries";
|
||||||
|
|
||||||
|
|
||||||
interface AuthData {
|
interface AuthData {
|
||||||
user: Player | undefined;
|
user: Player | undefined;
|
||||||
metadata: { accentColor: MantineColor; colorScheme: MantineColorScheme };
|
metadata: { accentColor: MantineColor; colorScheme: MantineColorScheme };
|
||||||
roles: string[];
|
roles: string[];
|
||||||
|
phone: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultAuthData: AuthData = {
|
export const defaultAuthData: AuthData = {
|
||||||
user: undefined,
|
user: undefined,
|
||||||
metadata: { accentColor: 'blue', colorScheme: 'auto' },
|
metadata: { accentColor: "blue", colorScheme: "dark" },
|
||||||
roles: [],
|
roles: [],
|
||||||
}
|
phone: ""
|
||||||
|
};
|
||||||
|
|
||||||
export interface AuthContextType extends AuthData {
|
export interface AuthContextType extends AuthData {
|
||||||
set: ({ user, metadata, roles }: Partial<AuthContextType>) => void;
|
set: ({ user, metadata, roles }: Partial<AuthContextType>) => void;
|
||||||
@@ -30,36 +37,43 @@ export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { data } = useMe();
|
const { data } = useMe();
|
||||||
|
|
||||||
const set = useCallback((updates: Partial<AuthData>) => {
|
const set = useCallback(
|
||||||
queryClient.setQueryData(playerKeys.auth, (oldData: AuthData | undefined) => {
|
(updates: Partial<AuthData>) => {
|
||||||
|
queryClient.setQueryData(
|
||||||
|
playerKeys.auth,
|
||||||
|
(oldData: AuthData | undefined) => {
|
||||||
const currentData = oldData || defaultAuthData;
|
const currentData = oldData || defaultAuthData;
|
||||||
return {
|
return {
|
||||||
...currentData,
|
...currentData,
|
||||||
...updates,
|
...updates,
|
||||||
metadata: updates.metadata
|
metadata: updates.metadata
|
||||||
? { ...currentData.metadata, ...updates.metadata }
|
? { ...currentData.metadata, ...updates.metadata }
|
||||||
: currentData.metadata
|
: currentData.metadata,
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
}, [queryClient]);
|
);
|
||||||
|
},
|
||||||
|
[queryClient]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
const value = useMemo(
|
||||||
<AuthContext
|
() => ({
|
||||||
value={{
|
user: data?.user,
|
||||||
user: data?.user || defaultAuthData.user,
|
metadata: data?.metadata || { accentColor: "blue" as MantineColor, colorScheme: "dark" as MantineColorScheme },
|
||||||
metadata: data?.metadata || defaultAuthData.metadata,
|
roles: data?.roles || [],
|
||||||
roles: data?.roles || defaultAuthData.roles,
|
phone: data?.phone || "",
|
||||||
set
|
set,
|
||||||
}}>
|
}),
|
||||||
{children}
|
[data, set]
|
||||||
</AuthContext>
|
);
|
||||||
)
|
|
||||||
|
return <AuthContext value={value}>{children}</AuthContext>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useAuth = () => {
|
export const useAuth = () => {
|
||||||
const context = useContext(AuthContext);
|
const context = useContext(AuthContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error('useAuth must be used within an AuthProvider');
|
throw new Error("useAuth must be used within an AuthProvider");
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
};
|
};
|
||||||
|
|||||||
473
src/contexts/spotify-context.tsx
Normal file
473
src/contexts/spotify-context.tsx
Normal file
@@ -0,0 +1,473 @@
|
|||||||
|
import { createContext, useCallback, useEffect, useState, PropsWithChildren } from 'react';
|
||||||
|
import { SpotifyAuth } from '@/lib/spotify/auth';
|
||||||
|
import { useAuth } from './auth-context';
|
||||||
|
import type {
|
||||||
|
SpotifyContextType,
|
||||||
|
SpotifyAuthState,
|
||||||
|
SpotifyDevice,
|
||||||
|
SpotifyPlaybackState,
|
||||||
|
SpotifyPlaybackSnapshot,
|
||||||
|
SpotifyTrack,
|
||||||
|
} from '@/lib/spotify/types';
|
||||||
|
|
||||||
|
const defaultSpotifyState: SpotifyAuthState = {
|
||||||
|
isAuthenticated: false,
|
||||||
|
accessToken: null,
|
||||||
|
refreshToken: null,
|
||||||
|
expiresAt: null,
|
||||||
|
scopes: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SpotifyContext = createContext<SpotifyContextType | null>(null);
|
||||||
|
|
||||||
|
export const SpotifyProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
||||||
|
const { roles } = useAuth();
|
||||||
|
const isAdmin = roles?.includes('Admin') || false;
|
||||||
|
|
||||||
|
const [authState, setAuthState] = useState<SpotifyAuthState>(defaultSpotifyState);
|
||||||
|
|
||||||
|
const [currentTrack, setCurrentTrack] = useState<SpotifyTrack | null>(null);
|
||||||
|
const [playbackState, setPlaybackState] = useState<SpotifyPlaybackState | null>(null);
|
||||||
|
|
||||||
|
const [devices, setDevices] = useState<SpotifyDevice[]>([]);
|
||||||
|
const [activeDevice, setActiveDeviceState] = useState<SpotifyDevice | null>(null);
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const [capturedState, setCapturedState] = useState<SpotifyPlaybackSnapshot | null>(null);
|
||||||
|
const [isCaptureLoading, setIsCaptureLoading] = useState(false);
|
||||||
|
const [isResumeLoading, setIsResumeLoading] = useState(false);
|
||||||
|
|
||||||
|
const spotifyAuth = new SpotifyAuth(
|
||||||
|
import.meta.env.VITE_SPOTIFY_CLIENT_ID!,
|
||||||
|
import.meta.env.VITE_SPOTIFY_REDIRECT_URI!
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isAdmin) {
|
||||||
|
checkExistingAuth();
|
||||||
|
handleOAuthCallback();
|
||||||
|
}
|
||||||
|
}, [isAdmin]);
|
||||||
|
|
||||||
|
const checkExistingAuth = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/spotify/token', {
|
||||||
|
credentials: 'include',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.access_token) {
|
||||||
|
setAuthState({
|
||||||
|
isAuthenticated: true,
|
||||||
|
accessToken: data.access_token,
|
||||||
|
refreshToken: data.refresh_token,
|
||||||
|
expiresAt: Date.now() + (3600 * 1000),
|
||||||
|
scopes: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all([getDevices(), refreshPlaybackState()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to check existing auth:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOAuthCallback = () => {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const spotifyAuth = urlParams.get('spotify_auth');
|
||||||
|
const error = urlParams.get('spotify_error');
|
||||||
|
const details = urlParams.get('details');
|
||||||
|
|
||||||
|
if (spotifyAuth === 'success') {
|
||||||
|
checkExistingAuth();
|
||||||
|
|
||||||
|
const newUrl = new URL(window.location.href);
|
||||||
|
newUrl.searchParams.delete('spotify_auth');
|
||||||
|
newUrl.searchParams.delete('state');
|
||||||
|
window.history.replaceState({}, '', newUrl.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
let errorMessage = `Authentication failed: ${error}`;
|
||||||
|
if (details) {
|
||||||
|
errorMessage += ` - ${decodeURIComponent(details)}`;
|
||||||
|
}
|
||||||
|
setError(errorMessage);
|
||||||
|
|
||||||
|
console.error('Spotify OAuth Error:', { error, details });
|
||||||
|
|
||||||
|
const newUrl = new URL(window.location.href);
|
||||||
|
newUrl.searchParams.delete('spotify_error');
|
||||||
|
newUrl.searchParams.delete('details');
|
||||||
|
window.history.replaceState({}, '', newUrl.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const login = useCallback(() => {
|
||||||
|
if (!isAdmin) return;
|
||||||
|
spotifyAuth.startAuthFlow(window.location.pathname);
|
||||||
|
}, [isAdmin, spotifyAuth]);
|
||||||
|
|
||||||
|
const logout = useCallback(() => {
|
||||||
|
setAuthState(defaultSpotifyState);
|
||||||
|
setCurrentTrack(null);
|
||||||
|
setPlaybackState(null);
|
||||||
|
setDevices([]);
|
||||||
|
setActiveDeviceState(null);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
document.cookie = 'spotify_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
|
||||||
|
document.cookie = 'spotify_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const makeSpotifyRequest = async (endpoint: string, options: RequestInit = {}) => {
|
||||||
|
const response = await fetch(`/api/spotify/${endpoint}`, {
|
||||||
|
...options,
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
let errorMessage = 'Request failed';
|
||||||
|
try {
|
||||||
|
const errorData = await response.json();
|
||||||
|
errorMessage = errorData.error || errorMessage;
|
||||||
|
} catch {
|
||||||
|
errorMessage = `HTTP ${response.status}: ${response.statusText}`;
|
||||||
|
}
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status === 204 || response.headers.get('content-length') === '0') {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentType = response.headers.get('content-type') || '';
|
||||||
|
if (!contentType.includes('application/json')) {
|
||||||
|
console.warn(`Non-JSON response from ${endpoint}:`, contentType);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Failed to parse JSON response from ${endpoint}:`, error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const play = useCallback(async (deviceId?: string) => {
|
||||||
|
if (!authState.isAuthenticated) return;
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await makeSpotifyRequest('playback', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ action: 'play', deviceId }),
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(refreshPlaybackState, 500);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error && !error.message.includes('JSON')) {
|
||||||
|
setError(error.message);
|
||||||
|
}
|
||||||
|
console.warn('Playback action completed with warning:', error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [authState.isAuthenticated]);
|
||||||
|
|
||||||
|
const playTrack = useCallback(async (trackId: string, deviceId?: string, positionMs?: number) => {
|
||||||
|
if (!authState.isAuthenticated) return;
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await makeSpotifyRequest('playback', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ action: 'playTrack', trackId, deviceId, positionMs }),
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(refreshPlaybackState, 500);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error && !error.message.includes('JSON')) {
|
||||||
|
setError(error.message);
|
||||||
|
}
|
||||||
|
console.warn('Track playback action completed with warning:', error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [authState.isAuthenticated]);
|
||||||
|
|
||||||
|
const pause = useCallback(async () => {
|
||||||
|
if (!authState.isAuthenticated) return;
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await makeSpotifyRequest('playback', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ action: 'pause' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(refreshPlaybackState, 500);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error && !error.message.includes('JSON')) {
|
||||||
|
setError(error.message);
|
||||||
|
}
|
||||||
|
console.warn('Playback action completed with warning:', error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [authState.isAuthenticated]);
|
||||||
|
|
||||||
|
const skipNext = useCallback(async () => {
|
||||||
|
if (!authState.isAuthenticated) return;
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await makeSpotifyRequest('playback', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ action: 'next' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(refreshPlaybackState, 500);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error && !error.message.includes('JSON')) {
|
||||||
|
setError(error.message);
|
||||||
|
}
|
||||||
|
console.warn('Playback action completed with warning:', error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [authState.isAuthenticated]);
|
||||||
|
|
||||||
|
const skipPrevious = useCallback(async () => {
|
||||||
|
if (!authState.isAuthenticated) return;
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await makeSpotifyRequest('playback', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ action: 'previous' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(refreshPlaybackState, 500);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error && !error.message.includes('JSON')) {
|
||||||
|
setError(error.message);
|
||||||
|
}
|
||||||
|
console.warn('Playback action completed with warning:', error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [authState.isAuthenticated]);
|
||||||
|
|
||||||
|
const setVolume = useCallback(async (volumePercent: number) => {
|
||||||
|
if (!authState.isAuthenticated) return;
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await makeSpotifyRequest('playback', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ action: 'volume', volumePercent }),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
setError(error instanceof Error ? error.message : 'Failed to set volume');
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [authState.isAuthenticated]);
|
||||||
|
|
||||||
|
const getDevices = useCallback(async () => {
|
||||||
|
if (!authState.isAuthenticated) return;
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await makeSpotifyRequest('playback?type=devices');
|
||||||
|
setDevices(data.devices || []);
|
||||||
|
|
||||||
|
const active = data.devices?.find((d: SpotifyDevice) => d.is_active);
|
||||||
|
if (active) {
|
||||||
|
setActiveDeviceState(active);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setError(error instanceof Error ? error.message : 'Failed to get devices');
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [authState.isAuthenticated]);
|
||||||
|
|
||||||
|
const setActiveDevice = useCallback(async (deviceId: string) => {
|
||||||
|
if (!authState.isAuthenticated) return;
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await makeSpotifyRequest('playback', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ action: 'transfer', deviceId }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const device = devices.find(d => d.id === deviceId);
|
||||||
|
if (device) {
|
||||||
|
setActiveDeviceState(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(getDevices, 1000);
|
||||||
|
} catch (error) {
|
||||||
|
setError(error instanceof Error ? error.message : 'Failed to set active device');
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [authState.isAuthenticated, devices]);
|
||||||
|
|
||||||
|
const refreshPlaybackState = useCallback(async () => {
|
||||||
|
if (!authState.isAuthenticated) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await makeSpotifyRequest('playback?type=state');
|
||||||
|
const state = data.playbackState;
|
||||||
|
|
||||||
|
setPlaybackState(state);
|
||||||
|
setCurrentTrack(state?.item || null);
|
||||||
|
|
||||||
|
if (state?.device) {
|
||||||
|
setActiveDeviceState(state.device);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to refresh playback state:', error);
|
||||||
|
}
|
||||||
|
}, [authState.isAuthenticated]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!authState.isAuthenticated) return;
|
||||||
|
|
||||||
|
const interval = setInterval(refreshPlaybackState, 5000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [authState.isAuthenticated, refreshPlaybackState]);
|
||||||
|
|
||||||
|
const capturePlaybackState = useCallback(async () => {
|
||||||
|
if (!authState.isAuthenticated) return;
|
||||||
|
|
||||||
|
setIsCaptureLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await makeSpotifyRequest('capture', {
|
||||||
|
method: 'POST',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.snapshot) {
|
||||||
|
setCapturedState(response.snapshot);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setError(error instanceof Error ? error.message : 'Failed to capture playback state');
|
||||||
|
} finally {
|
||||||
|
setIsCaptureLoading(false);
|
||||||
|
}
|
||||||
|
}, [authState.isAuthenticated]);
|
||||||
|
|
||||||
|
const resumePlaybackState = useCallback(async () => {
|
||||||
|
if (!authState.isAuthenticated || !capturedState) return;
|
||||||
|
|
||||||
|
setIsResumeLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await makeSpotifyRequest('resume', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ snapshot: capturedState }),
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(refreshPlaybackState, 1000);
|
||||||
|
} catch (error) {
|
||||||
|
setError(error instanceof Error ? error.message : 'Failed to resume playback state');
|
||||||
|
} finally {
|
||||||
|
setIsResumeLoading(false);
|
||||||
|
}
|
||||||
|
}, [authState.isAuthenticated, capturedState, refreshPlaybackState]);
|
||||||
|
|
||||||
|
const clearCapturedState = useCallback(() => {
|
||||||
|
setCapturedState(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const searchTracks = useCallback(async (query: string): Promise<SpotifyTrack[]> => {
|
||||||
|
if (!query.trim()) return [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/spotify/search?q=${encodeURIComponent(query)}`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Search failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data.tracks || [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to search tracks:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const contextValue: SpotifyContextType = {
|
||||||
|
...authState,
|
||||||
|
currentTrack,
|
||||||
|
playbackState,
|
||||||
|
devices,
|
||||||
|
activeDevice,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
// Capture/Resume state
|
||||||
|
capturedState,
|
||||||
|
isCaptureLoading,
|
||||||
|
isResumeLoading,
|
||||||
|
login,
|
||||||
|
logout,
|
||||||
|
play,
|
||||||
|
playTrack,
|
||||||
|
pause,
|
||||||
|
skipNext,
|
||||||
|
skipPrevious,
|
||||||
|
setVolume,
|
||||||
|
getDevices,
|
||||||
|
setActiveDevice,
|
||||||
|
refreshPlaybackState,
|
||||||
|
// Capture/Resume methods
|
||||||
|
capturePlaybackState,
|
||||||
|
resumePlaybackState,
|
||||||
|
clearCapturedState,
|
||||||
|
// Search
|
||||||
|
searchTracks,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isAdmin) {
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SpotifyContext.Provider value={contextValue}>
|
||||||
|
{children}
|
||||||
|
</SpotifyContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user