fansunited-data-layer 0.13.0 → 0.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/fansunited/constants.js +5 -0
- package/api/fansunited/constants.js.map +1 -0
- package/api/fansunited/football/competition/index.js +21 -0
- package/api/fansunited/football/competition/index.js.map +1 -0
- package/api/fansunited/football/competition/transformer.js +41 -0
- package/api/fansunited/football/competition/transformer.js.map +1 -0
- package/api/fansunited/football/competitions/index.js +56 -0
- package/api/fansunited/football/competitions/index.js.map +1 -0
- package/api/fansunited/football/competitions/transformer.js +68 -0
- package/api/fansunited/football/competitions/transformer.js.map +1 -0
- package/api/fansunited/football/http.js +7 -0
- package/api/fansunited/football/http.js.map +1 -0
- package/api/fansunited/football/matches/index.js +132 -0
- package/api/fansunited/football/matches/index.js.map +1 -0
- package/api/fansunited/football/matches/transformer.js +148 -0
- package/api/fansunited/football/matches/transformer.js.map +1 -0
- package/api/fansunited/football/players/index.js +60 -0
- package/api/fansunited/football/players/index.js.map +1 -0
- package/api/fansunited/football/players/transformer.js +85 -0
- package/api/fansunited/football/players/transformer.js.map +1 -0
- package/api/fansunited/football/search/index.js +64 -0
- package/api/fansunited/football/search/index.js.map +1 -0
- package/api/fansunited/football/search/transformer.js +114 -0
- package/api/fansunited/football/search/transformer.js.map +1 -0
- package/api/fansunited/football/teams/index.js +66 -0
- package/api/fansunited/football/teams/index.js.map +1 -0
- package/api/fansunited/football/teams/transformer.js +53 -0
- package/api/fansunited/football/teams/transformer.js.map +1 -0
- package/api/fansunited/http.d.ts.map +1 -1
- package/api/fansunited/http.js +70 -0
- package/api/fansunited/http.js.map +1 -0
- package/api/fansunited/search/constants.js +5 -0
- package/api/fansunited/search/constants.js.map +1 -0
- package/api/fansunited/search/http.js +7 -0
- package/api/fansunited/search/http.js.map +1 -0
- package/api/fansunited/search/index.js +308 -0
- package/api/fansunited/search/index.js.map +1 -0
- package/api/fansunited/search/transformer.js +374 -0
- package/api/fansunited/search/transformer.js.map +1 -0
- package/api/fansunited-sdk/loyalty/matches.js +21 -0
- package/api/fansunited-sdk/loyalty/matches.js.map +1 -0
- package/api/fansunited-sdk/loyalty/template.js +52 -0
- package/api/fansunited-sdk/loyalty/template.js.map +1 -0
- package/api/fansunited-sdk/odds/matches.js +109 -0
- package/api/fansunited-sdk/odds/matches.js.map +1 -0
- package/api/sportal365-sports/basketball/games/index.js +32 -0
- package/api/sportal365-sports/basketball/games/index.js.map +1 -0
- package/api/sportal365-sports/basketball/games/transformers/game.js +137 -0
- package/api/sportal365-sports/basketball/games/transformers/game.js.map +1 -0
- package/api/sportal365-sports/basketball/http.js +7 -0
- package/api/sportal365-sports/basketball/http.js.map +1 -0
- package/api/sportal365-sports/constants.js +15 -0
- package/api/sportal365-sports/constants.js.map +1 -0
- package/api/sportal365-sports/football/competitions/index.js +52 -0
- package/api/sportal365-sports/football/competitions/index.js.map +1 -0
- package/api/sportal365-sports/football/competitions/utils.js +92 -0
- package/api/sportal365-sports/football/competitions/utils.js.map +1 -0
- package/api/sportal365-sports/football/http.js +7 -0
- package/api/sportal365-sports/football/http.js.map +1 -0
- package/api/sportal365-sports/football/matches/index.js +206 -0
- package/api/sportal365-sports/football/matches/index.js.map +1 -0
- package/api/sportal365-sports/football/matches/transformers/commentary.js +27 -0
- package/api/sportal365-sports/football/matches/transformers/commentary.js.map +1 -0
- package/api/sportal365-sports/football/matches/transformers/lineup.js +74 -0
- package/api/sportal365-sports/football/matches/transformers/lineup.js.map +1 -0
- package/api/sportal365-sports/football/matches/transformers/match-event.js +60 -0
- package/api/sportal365-sports/football/matches/transformers/match-event.js.map +1 -0
- package/api/sportal365-sports/football/matches/transformers/match.js +195 -0
- package/api/sportal365-sports/football/matches/transformers/match.js.map +1 -0
- package/api/sportal365-sports/football/matches/transformers/odds.js +11 -0
- package/api/sportal365-sports/football/matches/transformers/odds.js.map +1 -0
- package/api/sportal365-sports/football/matches/transformers/statistics.js +41 -0
- package/api/sportal365-sports/football/matches/transformers/statistics.js.map +1 -0
- package/api/sportal365-sports/football/search/index.js +42 -0
- package/api/sportal365-sports/football/search/index.js.map +1 -0
- package/api/sportal365-sports/football/search/search.transformer.js +166 -0
- package/api/sportal365-sports/football/search/search.transformer.js.map +1 -0
- package/api/sportal365-sports/football/standings/index.js +28 -0
- package/api/sportal365-sports/football/standings/index.js.map +1 -0
- package/api/sportal365-sports/football/standings/standing.transformer.js +107 -0
- package/api/sportal365-sports/football/standings/standing.transformer.js.map +1 -0
- package/api/sportal365-sports/football/statistics/index.js +154 -0
- package/api/sportal365-sports/football/statistics/index.js.map +1 -0
- package/api/sportal365-sports/football/statistics/player-career.transformer.js +77 -0
- package/api/sportal365-sports/football/statistics/player-career.transformer.js.map +1 -0
- package/api/sportal365-sports/football/statistics/player-recent.transformer.js +54 -0
- package/api/sportal365-sports/football/statistics/player-recent.transformer.js.map +1 -0
- package/api/sportal365-sports/football/statistics/player-season.transformer.js +147 -0
- package/api/sportal365-sports/football/statistics/player-season.transformer.js.map +1 -0
- package/api/sportal365-sports/football/teams/index.js +73 -0
- package/api/sportal365-sports/football/teams/index.js.map +1 -0
- package/api/sportal365-sports/football/teams/utils.js +196 -0
- package/api/sportal365-sports/football/teams/utils.js.map +1 -0
- package/api/sportal365-sports/http.js +58 -0
- package/api/sportal365-sports/http.js.map +1 -0
- package/api/sportal365-sports/search/http.js +7 -0
- package/api/sportal365-sports/search/http.js.map +1 -0
- package/api/sportal365-sports/search/index.js +61 -0
- package/api/sportal365-sports/search/index.js.map +1 -0
- package/api/sportal365-sports/search/search.transformer.js +254 -0
- package/api/sportal365-sports/search/search.transformer.js.map +1 -0
- package/api/sportal365-sports/shared/odds.transformer.js +65 -0
- package/api/sportal365-sports/shared/odds.transformer.js.map +1 -0
- package/api/sportal365-sports/shared/providerRef.helper.js +31 -0
- package/api/sportal365-sports/shared/providerRef.helper.js.map +1 -0
- package/api/sportal365-sports/standings/http.js +7 -0
- package/api/sportal365-sports/standings/http.js.map +1 -0
- package/api/sportal365-sports/standings/index.js +29 -0
- package/api/sportal365-sports/standings/index.js.map +1 -0
- package/api/sportal365-sports/standings/standing.transformer.js +185 -0
- package/api/sportal365-sports/standings/standing.transformer.js.map +1 -0
- package/api/sportal365-sports/statistics/http.js +7 -0
- package/api/sportal365-sports/statistics/http.js.map +1 -0
- package/api/sportal365-sports/statistics/index.js +25 -0
- package/api/sportal365-sports/statistics/index.js.map +1 -0
- package/api/sportal365-sports/statistics/team-stats.transformer.js +16 -0
- package/api/sportal365-sports/statistics/team-stats.transformer.js.map +1 -0
- package/api/sportal365-sports/tennis/http.js +7 -0
- package/api/sportal365-sports/tennis/http.js.map +1 -0
- package/api/sportal365-sports/tennis/matches/index.js +34 -0
- package/api/sportal365-sports/tennis/matches/index.js.map +1 -0
- package/api/sportal365-sports/tennis/matches/transformers/match.js +193 -0
- package/api/sportal365-sports/tennis/matches/transformers/match.js.map +1 -0
- package/cache/cache-manager.js +136 -0
- package/cache/cache-manager.js.map +1 -0
- package/cache/memory-store.js +29 -0
- package/cache/memory-store.js.map +1 -0
- package/client.js +38 -12337
- package/client.js.map +1 -0
- package/config/index.js +20 -0
- package/config/index.js.map +1 -0
- package/fansunited-data-layer.js +146 -3553
- package/fansunited-data-layer.js.map +1 -0
- package/helpers/competition.helpers.js +11 -0
- package/helpers/competition.helpers.js.map +1 -0
- package/helpers/player.helpers.js +165 -0
- package/helpers/player.helpers.js.map +1 -0
- package/helpers/player.hooks.js +34 -0
- package/helpers/player.hooks.js.map +1 -0
- package/helpers/team.helpers.js +216 -0
- package/helpers/team.helpers.js.map +1 -0
- package/package.json +6 -8
- package/providers/competition/hooks/useCompetitionStats.js +10 -0
- package/providers/competition/hooks/useCompetitionStats.js.map +1 -0
- package/providers/competition/hooks/useGoalDistribution.js +23 -0
- package/providers/competition/hooks/useGoalDistribution.js.map +1 -0
- package/providers/competition/hooks/useMatchHelpers.js +85 -0
- package/providers/competition/hooks/useMatchHelpers.js.map +1 -0
- package/providers/competition/hooks/useStandingsCalculations.js +89 -0
- package/providers/competition/hooks/useStandingsCalculations.js.map +1 -0
- package/providers/competition/hooks/useStandingsHelpers.js +61 -0
- package/providers/competition/hooks/useStandingsHelpers.js.map +1 -0
- package/providers/competition/hooks/useTeamPosition.js +15 -0
- package/providers/competition/hooks/useTeamPosition.js.map +1 -0
- package/providers/competition/hooks/useTeamResultsTable.js +86 -0
- package/providers/competition/hooks/useTeamResultsTable.js.map +1 -0
- package/providers/competition/utils/competitionStats.js +109 -0
- package/providers/competition/utils/competitionStats.js.map +1 -0
- package/providers/competition/utils/goalDistribution.js +64 -0
- package/providers/competition/utils/goalDistribution.js.map +1 -0
- package/providers/competition/utils/standingsCalculations.js +152 -0
- package/providers/competition/utils/standingsCalculations.js.map +1 -0
- package/providers/competition.context.js +14 -0
- package/providers/competition.context.js.map +1 -0
- package/providers/competition.provider.js +187 -0
- package/providers/competition.provider.js.map +1 -0
- package/providers/fansunited-config.context.js +6 -0
- package/providers/fansunited-config.context.js.map +1 -0
- package/providers/fansunited-config.hooks.js +20 -0
- package/providers/fansunited-config.hooks.js.map +1 -0
- package/providers/fansunited-config.provider.js +9 -0
- package/providers/fansunited-config.provider.js.map +1 -0
- package/providers/fansunited-sdk.hook.js +50 -0
- package/providers/fansunited-sdk.hook.js.map +1 -0
- package/providers/leaderboard/hooks/useMatchHelpers.js +30 -0
- package/providers/leaderboard/hooks/useMatchHelpers.js.map +1 -0
- package/providers/leaderboard/hooks/useTemplateHelpers.js +39 -0
- package/providers/leaderboard/hooks/useTemplateHelpers.js.map +1 -0
- package/providers/leaderboard.context.js +14 -0
- package/providers/leaderboard.context.js.map +1 -0
- package/providers/leaderboard.provider.js +98 -0
- package/providers/leaderboard.provider.js.map +1 -0
- package/providers/match/hooks/useBatchMatchOdds.js +72 -0
- package/providers/match/hooks/useBatchMatchOdds.js.map +1 -0
- package/providers/match/hooks/useEventHelpers.js +46 -0
- package/providers/match/hooks/useEventHelpers.js.map +1 -0
- package/providers/match/hooks/useHeadToHeadHelpers.js +52 -0
- package/providers/match/hooks/useHeadToHeadHelpers.js.map +1 -0
- package/providers/match/hooks/useLineupHelpers.js +53 -0
- package/providers/match/hooks/useLineupHelpers.js.map +1 -0
- package/providers/match/hooks/useMatchStandingsCalculations.js +332 -0
- package/providers/match/hooks/useMatchStandingsCalculations.js.map +1 -0
- package/providers/match/hooks/useMatchStatus.js +37 -0
- package/providers/match/hooks/useMatchStatus.js.map +1 -0
- package/providers/match/hooks/useOddsHelpers.js +54 -0
- package/providers/match/hooks/useOddsHelpers.js.map +1 -0
- package/providers/match/hooks/useScoreHelpers.js +34 -0
- package/providers/match/hooks/useScoreHelpers.js.map +1 -0
- package/providers/match/hooks/useStandingsHelpers.js +53 -0
- package/providers/match/hooks/useStandingsHelpers.js.map +1 -0
- package/providers/match/hooks/useStatisticsHelpers.js +40 -0
- package/providers/match/hooks/useStatisticsHelpers.js.map +1 -0
- package/providers/match/hooks/useTeamHelpers.js +38 -0
- package/providers/match/hooks/useTeamHelpers.js.map +1 -0
- package/providers/match/hooks/useTeamStatsHelpers.js +141 -0
- package/providers/match/hooks/useTeamStatsHelpers.js.map +1 -0
- package/providers/match.context.js +14 -0
- package/providers/match.context.js.map +1 -0
- package/providers/match.provider.js +176 -0
- package/providers/match.provider.js.map +1 -0
- package/providers/team/hooks/useFormStats.js +116 -0
- package/providers/team/hooks/useFormStats.js.map +1 -0
- package/providers/team/hooks/useMatchHelpers.js +46 -0
- package/providers/team/hooks/useMatchHelpers.js.map +1 -0
- package/providers/team/hooks/useSquadHelpers.js +67 -0
- package/providers/team/hooks/useSquadHelpers.js.map +1 -0
- package/providers/team.context.js +14 -0
- package/providers/team.context.js.map +1 -0
- package/providers/team.provider.js +98 -0
- package/providers/team.provider.js.map +1 -0
- package/utilities/stats/core/helpers.js +105 -0
- package/utilities/stats/core/helpers.js.map +1 -0
- package/utilities/stats/core/types.js +5 -0
- package/utilities/stats/core/types.js.map +1 -0
- package/utilities/stats/match/headToHead.js +83 -0
- package/utilities/stats/match/headToHead.js.map +1 -0
- package/utilities/stats/match/homeVsAway.js +140 -0
- package/utilities/stats/match/homeVsAway.js.map +1 -0
- package/utilities/stats/match/overUnder.js +91 -0
- package/utilities/stats/match/overUnder.js.map +1 -0
- package/utilities/stats/match/result.js +21 -0
- package/utilities/stats/match/result.js.map +1 -0
- package/utilities/stats/team/commonOpponents.js +77 -0
- package/utilities/stats/team/commonOpponents.js.map +1 -0
- package/utilities/stats/team/goalStats.js +50 -0
- package/utilities/stats/team/goalStats.js.map +1 -0
- package/utilities/stats/team/streaks.js +183 -0
- package/utilities/stats/team/streaks.js.map +1 -0
- package/client.cjs +0 -8
- package/fansunited-data-layer.cjs +0 -1
- package/matches-BvHU-bP0.js +0 -1795
- package/matches-Ci0Ci_Mw.cjs +0 -1
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
function calculateFormStandings(matches, teamIds, getTeamInfo, matchCount = 8, filterFn) {
|
|
2
|
+
const entries = Array.from(teamIds).map((teamId) => {
|
|
3
|
+
const competitor = getTeamInfo(teamId);
|
|
4
|
+
if (!competitor) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
let teamMatches = matches.filter((m) => m.competitorOne.id === teamId || m.competitorTwo.id === teamId);
|
|
8
|
+
if (filterFn) {
|
|
9
|
+
teamMatches = teamMatches.filter((m) => filterFn(m, teamId));
|
|
10
|
+
}
|
|
11
|
+
teamMatches = teamMatches.slice(0, matchCount);
|
|
12
|
+
let points = 0;
|
|
13
|
+
let played = 0;
|
|
14
|
+
let won = 0;
|
|
15
|
+
let drawn = 0;
|
|
16
|
+
let lost = 0;
|
|
17
|
+
teamMatches.forEach((match) => {
|
|
18
|
+
const isCompetitorOne = match.competitorOne.id === teamId;
|
|
19
|
+
const teamScore = parseInt(
|
|
20
|
+
isCompetitorOne ? match.score?.competitorOne ?? "0" : match.score?.competitorTwo ?? "0"
|
|
21
|
+
);
|
|
22
|
+
const opponentScore = parseInt(
|
|
23
|
+
isCompetitorOne ? match.score?.competitorTwo ?? "0" : match.score?.competitorOne ?? "0"
|
|
24
|
+
);
|
|
25
|
+
played++;
|
|
26
|
+
if (teamScore > opponentScore) {
|
|
27
|
+
won++;
|
|
28
|
+
points += 3;
|
|
29
|
+
} else if (teamScore === opponentScore) {
|
|
30
|
+
drawn++;
|
|
31
|
+
points += 1;
|
|
32
|
+
} else {
|
|
33
|
+
lost++;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
rank: 0,
|
|
38
|
+
competitor,
|
|
39
|
+
stats: {
|
|
40
|
+
played,
|
|
41
|
+
won,
|
|
42
|
+
drawn,
|
|
43
|
+
lost,
|
|
44
|
+
points
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}).filter((entry) => entry !== null);
|
|
48
|
+
entries.sort((a, b) => (b.stats.points ?? 0) - (a.stats.points ?? 0));
|
|
49
|
+
entries.forEach((entry, index) => {
|
|
50
|
+
entry.rank = index + 1;
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
entries,
|
|
54
|
+
metadata: { legend: [] }
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function calculateOffenceStandings(matches, teamIds, getTeamInfo, matchCount, filterFn) {
|
|
58
|
+
const entries = Array.from(teamIds).map((teamId) => {
|
|
59
|
+
const competitor = getTeamInfo(teamId);
|
|
60
|
+
if (!competitor) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
let teamMatches = matches.filter((m) => m.competitorOne.id === teamId || m.competitorTwo.id === teamId);
|
|
64
|
+
if (filterFn) {
|
|
65
|
+
teamMatches = teamMatches.filter((m) => filterFn(m, teamId));
|
|
66
|
+
}
|
|
67
|
+
if (matchCount !== void 0) {
|
|
68
|
+
teamMatches = teamMatches.slice(0, matchCount);
|
|
69
|
+
}
|
|
70
|
+
let goalsFor = 0;
|
|
71
|
+
let played = 0;
|
|
72
|
+
teamMatches.forEach((match) => {
|
|
73
|
+
const isCompetitorOne = match.competitorOne.id === teamId;
|
|
74
|
+
const teamScore = parseInt(
|
|
75
|
+
isCompetitorOne ? match.score?.competitorOne ?? "0" : match.score?.competitorTwo ?? "0"
|
|
76
|
+
);
|
|
77
|
+
goalsFor += teamScore;
|
|
78
|
+
played++;
|
|
79
|
+
});
|
|
80
|
+
return {
|
|
81
|
+
rank: 0,
|
|
82
|
+
competitor,
|
|
83
|
+
stats: {
|
|
84
|
+
played,
|
|
85
|
+
goalsFor
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}).filter((entry) => entry !== null);
|
|
89
|
+
entries.sort((a, b) => (b.stats.goalsFor ?? 0) - (a.stats.goalsFor ?? 0));
|
|
90
|
+
entries.forEach((entry, index) => {
|
|
91
|
+
entry.rank = index + 1;
|
|
92
|
+
});
|
|
93
|
+
return {
|
|
94
|
+
entries,
|
|
95
|
+
metadata: { legend: [] }
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function calculateDefenceStandings(matches, teamIds, getTeamInfo, matchCount, filterFn) {
|
|
99
|
+
const entries = Array.from(teamIds).map((teamId) => {
|
|
100
|
+
const competitor = getTeamInfo(teamId);
|
|
101
|
+
if (!competitor) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
let teamMatches = matches.filter((m) => m.competitorOne.id === teamId || m.competitorTwo.id === teamId);
|
|
105
|
+
if (filterFn) {
|
|
106
|
+
teamMatches = teamMatches.filter((m) => filterFn(m, teamId));
|
|
107
|
+
}
|
|
108
|
+
if (matchCount !== void 0) {
|
|
109
|
+
teamMatches = teamMatches.slice(0, matchCount);
|
|
110
|
+
}
|
|
111
|
+
let goalsAgainst = 0;
|
|
112
|
+
let played = 0;
|
|
113
|
+
teamMatches.forEach((match) => {
|
|
114
|
+
const isCompetitorOne = match.competitorOne.id === teamId;
|
|
115
|
+
const opponentScore = parseInt(
|
|
116
|
+
isCompetitorOne ? match.score?.competitorTwo ?? "0" : match.score?.competitorOne ?? "0"
|
|
117
|
+
);
|
|
118
|
+
goalsAgainst += opponentScore;
|
|
119
|
+
played++;
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
rank: 0,
|
|
123
|
+
competitor,
|
|
124
|
+
stats: {
|
|
125
|
+
played,
|
|
126
|
+
goalsAgainst
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}).filter((entry) => entry !== null);
|
|
130
|
+
entries.sort((a, b) => (a.stats.goalsAgainst ?? 0) - (b.stats.goalsAgainst ?? 0));
|
|
131
|
+
entries.forEach((entry, index) => {
|
|
132
|
+
entry.rank = index + 1;
|
|
133
|
+
});
|
|
134
|
+
return {
|
|
135
|
+
entries,
|
|
136
|
+
metadata: { legend: [] }
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const isHomeMatch = (match, teamId) => {
|
|
140
|
+
return match.competitorOne.id === teamId;
|
|
141
|
+
};
|
|
142
|
+
const isAwayMatch = (match, teamId) => {
|
|
143
|
+
return match.competitorTwo.id === teamId;
|
|
144
|
+
};
|
|
145
|
+
export {
|
|
146
|
+
calculateDefenceStandings,
|
|
147
|
+
calculateFormStandings,
|
|
148
|
+
calculateOffenceStandings,
|
|
149
|
+
isAwayMatch,
|
|
150
|
+
isHomeMatch
|
|
151
|
+
};
|
|
152
|
+
//# sourceMappingURL=standingsCalculations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standingsCalculations.js","sources":["../../../../src/lib/providers/competition/utils/standingsCalculations.ts"],"sourcesContent":["import type { FUSportsMatch, FUSportsStandingEntry, FUSportsStandings } from \"../../../types/canonical\";\n\n/**\n * Calculate form-based standings from finished matches\n * @param matches - All finished matches (should be pre-sorted by date descending)\n * @param teamIds - Set of team IDs to calculate standings for\n * @param getTeamInfo - Function to get team competitor info\n * @param matchCount - Number of recent matches to consider\n * @param filterFn - Optional filter function to filter matches (e.g., home/away only)\n */\nexport function calculateFormStandings(\n matches: FUSportsMatch[],\n teamIds: Set<string>,\n getTeamInfo: (teamId: string) => FUSportsStandingEntry[\"competitor\"] | undefined,\n matchCount: number = 8,\n filterFn?: (match: FUSportsMatch, teamId: string) => boolean\n): FUSportsStandings {\n const entries = Array.from(teamIds)\n .map((teamId) => {\n const competitor = getTeamInfo(teamId);\n if (!competitor) {\n return null;\n }\n\n // Filter matches for this team\n let teamMatches = matches.filter((m) => m.competitorOne.id === teamId || m.competitorTwo.id === teamId);\n\n // Apply additional filter if provided (e.g., home/away only)\n if (filterFn) {\n teamMatches = teamMatches.filter((m) => filterFn(m, teamId));\n }\n\n // Take only the last N matches\n teamMatches = teamMatches.slice(0, matchCount);\n\n let points = 0;\n let played = 0;\n let won = 0;\n let drawn = 0;\n let lost = 0;\n\n teamMatches.forEach((match) => {\n const isCompetitorOne = match.competitorOne.id === teamId;\n const teamScore = parseInt(\n isCompetitorOne ? (match.score?.competitorOne ?? \"0\") : (match.score?.competitorTwo ?? \"0\")\n );\n const opponentScore = parseInt(\n isCompetitorOne ? (match.score?.competitorTwo ?? \"0\") : (match.score?.competitorOne ?? \"0\")\n );\n\n played++;\n if (teamScore > opponentScore) {\n won++;\n points += 3;\n } else if (teamScore === opponentScore) {\n drawn++;\n points += 1;\n } else {\n lost++;\n }\n });\n\n return {\n rank: 0,\n competitor,\n stats: {\n played,\n won,\n drawn,\n lost,\n points,\n },\n } as FUSportsStandingEntry;\n })\n .filter((entry): entry is FUSportsStandingEntry => entry !== null);\n\n // Sort by points and assign ranks\n entries.sort((a, b) => (b.stats.points ?? 0) - (a.stats.points ?? 0));\n entries.forEach((entry, index) => {\n entry.rank = index + 1;\n });\n\n return {\n entries,\n metadata: { legend: [] },\n };\n}\n\n/**\n * Calculate offence standings (goals scored)\n */\nexport function calculateOffenceStandings(\n matches: FUSportsMatch[],\n teamIds: Set<string>,\n getTeamInfo: (teamId: string) => FUSportsStandingEntry[\"competitor\"] | undefined,\n matchCount?: number,\n filterFn?: (match: FUSportsMatch, teamId: string) => boolean\n): FUSportsStandings {\n const entries = Array.from(teamIds)\n .map((teamId) => {\n const competitor = getTeamInfo(teamId);\n if (!competitor) {\n return null;\n }\n\n let teamMatches = matches.filter((m) => m.competitorOne.id === teamId || m.competitorTwo.id === teamId);\n\n if (filterFn) {\n teamMatches = teamMatches.filter((m) => filterFn(m, teamId));\n }\n\n if (matchCount !== undefined) {\n teamMatches = teamMatches.slice(0, matchCount);\n }\n\n let goalsFor = 0;\n let played = 0;\n\n teamMatches.forEach((match) => {\n const isCompetitorOne = match.competitorOne.id === teamId;\n const teamScore = parseInt(\n isCompetitorOne ? (match.score?.competitorOne ?? \"0\") : (match.score?.competitorTwo ?? \"0\")\n );\n goalsFor += teamScore;\n played++;\n });\n\n return {\n rank: 0,\n competitor,\n stats: {\n played,\n goalsFor,\n },\n } as FUSportsStandingEntry;\n })\n .filter((entry): entry is FUSportsStandingEntry => entry !== null);\n\n entries.sort((a, b) => (b.stats.goalsFor ?? 0) - (a.stats.goalsFor ?? 0));\n entries.forEach((entry, index) => {\n entry.rank = index + 1;\n });\n\n return {\n entries,\n metadata: { legend: [] },\n };\n}\n\n/**\n * Calculate defence standings (goals conceded)\n */\nexport function calculateDefenceStandings(\n matches: FUSportsMatch[],\n teamIds: Set<string>,\n getTeamInfo: (teamId: string) => FUSportsStandingEntry[\"competitor\"] | undefined,\n matchCount?: number,\n filterFn?: (match: FUSportsMatch, teamId: string) => boolean\n): FUSportsStandings {\n const entries = Array.from(teamIds)\n .map((teamId) => {\n const competitor = getTeamInfo(teamId);\n if (!competitor) {\n return null;\n }\n\n let teamMatches = matches.filter((m) => m.competitorOne.id === teamId || m.competitorTwo.id === teamId);\n\n if (filterFn) {\n teamMatches = teamMatches.filter((m) => filterFn(m, teamId));\n }\n\n if (matchCount !== undefined) {\n teamMatches = teamMatches.slice(0, matchCount);\n }\n\n let goalsAgainst = 0;\n let played = 0;\n\n teamMatches.forEach((match) => {\n const isCompetitorOne = match.competitorOne.id === teamId;\n const opponentScore = parseInt(\n isCompetitorOne ? (match.score?.competitorTwo ?? \"0\") : (match.score?.competitorOne ?? \"0\")\n );\n goalsAgainst += opponentScore;\n played++;\n });\n\n return {\n rank: 0,\n competitor,\n stats: {\n played,\n goalsAgainst,\n },\n } as FUSportsStandingEntry;\n })\n .filter((entry): entry is FUSportsStandingEntry => entry !== null);\n\n // Sort by fewest goals against (best defence first)\n entries.sort((a, b) => (a.stats.goalsAgainst ?? 0) - (b.stats.goalsAgainst ?? 0));\n entries.forEach((entry, index) => {\n entry.rank = index + 1;\n });\n\n return {\n entries,\n metadata: { legend: [] },\n };\n}\n\n/**\n * Filter functions for home/away matches\n */\nexport const isHomeMatch = (match: FUSportsMatch, teamId: string): boolean => {\n return match.competitorOne.id === teamId;\n};\n\nexport const isAwayMatch = (match: FUSportsMatch, teamId: string): boolean => {\n return match.competitorTwo.id === teamId;\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
const CompetitionContext = createContext(null);
|
|
3
|
+
function useCompetition() {
|
|
4
|
+
const context = useContext(CompetitionContext);
|
|
5
|
+
if (!context) {
|
|
6
|
+
throw new Error("useCompetition must be used within CompetitionProvider");
|
|
7
|
+
}
|
|
8
|
+
return context;
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
CompetitionContext,
|
|
12
|
+
useCompetition
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=competition.context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"competition.context.js","sources":["../../src/lib/providers/competition.context.ts"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { CompetitionContextValue } from \"./competition.types\";\n\n// ============================================================================\n// Context\n// ============================================================================\n\nexport const CompetitionContext = createContext<CompetitionContextValue | null>(null);\n\n// ============================================================================\n// Hook\n// ============================================================================\n\n/**\n * Hook to access competition context\n * Must be used within a CompetitionProvider\n */\nexport function useCompetition(): CompetitionContextValue {\n const context = useContext(CompetitionContext);\n if (!context) {\n throw new Error(\"useCompetition must be used within CompetitionProvider\");\n }\n return context;\n}\n"],"names":[],"mappings":";AASO,MAAM,qBAAqB,cAA8C,IAAI;AAU7E,SAAS,iBAA0C;AACtD,QAAM,UAAU,WAAW,kBAAkB;AAC7C,MAAI,CAAC,SAAS;AACV,UAAM,IAAI,MAAM,wDAAwD;AAAA,EAC5E;AACA,SAAO;AACX;"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useCallback, useMemo } from "react";
|
|
3
|
+
import { CompetitionContext } from "./competition.context.js";
|
|
4
|
+
import { getFootballCompetition, getFootballSeasonDetails } from "../api/sportal365-sports/football/competitions/index.js";
|
|
5
|
+
import { getFootballMatches } from "../api/sportal365-sports/football/matches/index.js";
|
|
6
|
+
import { getFootballStandings } from "../api/sportal365-sports/football/standings/index.js";
|
|
7
|
+
import { useMatchHelpers } from "./competition/hooks/useMatchHelpers.js";
|
|
8
|
+
import { useStandingsHelpers } from "./competition/hooks/useStandingsHelpers.js";
|
|
9
|
+
import { useStandingsCalculations } from "./competition/hooks/useStandingsCalculations.js";
|
|
10
|
+
import { useGoalDistribution } from "./competition/hooks/useGoalDistribution.js";
|
|
11
|
+
import { useCompetitionStats } from "./competition/hooks/useCompetitionStats.js";
|
|
12
|
+
import { useTeamResultsTable } from "./competition/hooks/useTeamResultsTable.js";
|
|
13
|
+
import { useTeamPosition } from "./competition/hooks/useTeamPosition.js";
|
|
14
|
+
import { useDataLayerConfig } from "./fansunited-config.hooks.js";
|
|
15
|
+
function CompetitionProvider({
|
|
16
|
+
competitionId,
|
|
17
|
+
seasonId,
|
|
18
|
+
stageId,
|
|
19
|
+
competition: initialCompetition,
|
|
20
|
+
season: initialSeason,
|
|
21
|
+
matches: initialMatches,
|
|
22
|
+
standings: initialStandings,
|
|
23
|
+
previousSeasons,
|
|
24
|
+
config,
|
|
25
|
+
autoRefresh = true,
|
|
26
|
+
refreshInterval = 3e4,
|
|
27
|
+
children
|
|
28
|
+
}) {
|
|
29
|
+
const dataLayerConfig = useDataLayerConfig();
|
|
30
|
+
const [competition, setCompetition] = useState(initialCompetition);
|
|
31
|
+
const [season, setSeason] = useState(initialSeason);
|
|
32
|
+
const [matches, setMatches] = useState(initialMatches ?? []);
|
|
33
|
+
const [standings, setStandings] = useState(
|
|
34
|
+
initialStandings ?? { entries: [], metadata: { legend: [] } }
|
|
35
|
+
);
|
|
36
|
+
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
37
|
+
const [lastRefreshed, setLastRefreshed] = useState(void 0);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (competition && season) return;
|
|
40
|
+
const fetchInitialData = async () => {
|
|
41
|
+
try {
|
|
42
|
+
const targetSeasonId = seasonId ?? season?.id;
|
|
43
|
+
const [competitionData, seasonData] = await Promise.all([
|
|
44
|
+
!competition ? getFootballCompetition(competitionId, void 0, dataLayerConfig) : Promise.resolve(competition),
|
|
45
|
+
!season ? targetSeasonId ? getFootballSeasonDetails({ seasonId: targetSeasonId }, dataLayerConfig) : getFootballSeasonDetails({ competitionId, status: "CURRENT" }, dataLayerConfig) : Promise.resolve(season)
|
|
46
|
+
]);
|
|
47
|
+
if (!competition) setCompetition(competitionData);
|
|
48
|
+
if (!season) setSeason(seasonData);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error("[CompetitionProvider] Error fetching initial data:", error);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
fetchInitialData();
|
|
54
|
+
}, [competitionId, seasonId, dataLayerConfig]);
|
|
55
|
+
const refresh = useCallback(async () => {
|
|
56
|
+
const effectiveSeasonId = seasonId ?? season?.id;
|
|
57
|
+
if (!effectiveSeasonId) return;
|
|
58
|
+
setIsRefreshing(true);
|
|
59
|
+
try {
|
|
60
|
+
const effectiveStageId = stageId ?? season?.stages?.find((s) => s.status === "ACTIVE")?.id;
|
|
61
|
+
const [matchesData, standingsData] = await Promise.all([
|
|
62
|
+
getFootballMatches(
|
|
63
|
+
{
|
|
64
|
+
tournamentIds: [competitionId],
|
|
65
|
+
seasonIds: [effectiveSeasonId],
|
|
66
|
+
stageIds: effectiveStageId ? [effectiveStageId] : void 0,
|
|
67
|
+
limit: 552,
|
|
68
|
+
offset: 0
|
|
69
|
+
},
|
|
70
|
+
dataLayerConfig
|
|
71
|
+
),
|
|
72
|
+
effectiveStageId ? getFootballStandings(
|
|
73
|
+
{
|
|
74
|
+
stageId: effectiveStageId,
|
|
75
|
+
expand: ["rules", "form.events"]
|
|
76
|
+
},
|
|
77
|
+
dataLayerConfig
|
|
78
|
+
) : Promise.resolve({ entries: [], metadata: { legend: [] } })
|
|
79
|
+
]);
|
|
80
|
+
setMatches(matchesData);
|
|
81
|
+
setStandings(standingsData);
|
|
82
|
+
setLastRefreshed(/* @__PURE__ */ new Date());
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error("[CompetitionProvider] Error refreshing matches/standings:", error);
|
|
85
|
+
} finally {
|
|
86
|
+
setIsRefreshing(false);
|
|
87
|
+
}
|
|
88
|
+
}, [competitionId, seasonId, stageId, season?.id, season?.stages, dataLayerConfig]);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
if (!autoRefresh) return;
|
|
91
|
+
const hasLiveMatches = matches.some((m) => m.status.code === "live");
|
|
92
|
+
if (!hasLiveMatches) return;
|
|
93
|
+
const interval = setInterval(() => {
|
|
94
|
+
refresh();
|
|
95
|
+
}, refreshInterval);
|
|
96
|
+
return () => clearInterval(interval);
|
|
97
|
+
}, [autoRefresh, refreshInterval, matches, refresh]);
|
|
98
|
+
const matchHelpers = useMatchHelpers(matches);
|
|
99
|
+
const standingsHelpers = useStandingsHelpers(standings);
|
|
100
|
+
const standingsCalculations = useStandingsCalculations(matches, standings, standingsHelpers.getTeamStanding);
|
|
101
|
+
const { goalDistribution, getGoalDistribution } = useGoalDistribution(matches);
|
|
102
|
+
const competitionStats = useCompetitionStats(matches);
|
|
103
|
+
const { getTeamResultsTable } = useTeamResultsTable(matches, standings);
|
|
104
|
+
const { getTeamPosition } = useTeamPosition(standings);
|
|
105
|
+
const getTeamForm = useCallback(
|
|
106
|
+
(teamId) => standingsHelpers.getTeamStanding(teamId)?.form,
|
|
107
|
+
[standingsHelpers]
|
|
108
|
+
);
|
|
109
|
+
const getSeasonMatches = useCallback(
|
|
110
|
+
(seasonId2) => previousSeasons?.find((s) => s.id === seasonId2)?.matches,
|
|
111
|
+
[previousSeasons]
|
|
112
|
+
);
|
|
113
|
+
const getSeasonStandings = useCallback(
|
|
114
|
+
(seasonId2) => previousSeasons?.find((s) => s.id === seasonId2)?.standings,
|
|
115
|
+
[previousSeasons]
|
|
116
|
+
);
|
|
117
|
+
const value = useMemo(() => {
|
|
118
|
+
return {
|
|
119
|
+
// Core identifiers
|
|
120
|
+
competitionId,
|
|
121
|
+
seasonId,
|
|
122
|
+
stageId,
|
|
123
|
+
// Competition and season info
|
|
124
|
+
competition,
|
|
125
|
+
season,
|
|
126
|
+
// Loading and refresh state
|
|
127
|
+
isRefreshing,
|
|
128
|
+
lastRefreshed,
|
|
129
|
+
// Core data
|
|
130
|
+
matches,
|
|
131
|
+
standings,
|
|
132
|
+
// Historical data
|
|
133
|
+
previousSeasons,
|
|
134
|
+
// Custom configuration
|
|
135
|
+
config,
|
|
136
|
+
// Refresh method
|
|
137
|
+
refresh,
|
|
138
|
+
// Match helpers
|
|
139
|
+
...matchHelpers,
|
|
140
|
+
// Standings helpers
|
|
141
|
+
...standingsHelpers,
|
|
142
|
+
getTeamForm,
|
|
143
|
+
getTeamPosition,
|
|
144
|
+
// Form-based standings helpers
|
|
145
|
+
...standingsCalculations,
|
|
146
|
+
// Goal distribution helpers
|
|
147
|
+
goalDistribution,
|
|
148
|
+
getGoalDistribution,
|
|
149
|
+
// Competition statistics
|
|
150
|
+
competitionStats,
|
|
151
|
+
// Historical helpers
|
|
152
|
+
getSeasonMatches,
|
|
153
|
+
getSeasonStandings,
|
|
154
|
+
// Team results table helper
|
|
155
|
+
getTeamResultsTable
|
|
156
|
+
};
|
|
157
|
+
}, [
|
|
158
|
+
competitionId,
|
|
159
|
+
seasonId,
|
|
160
|
+
stageId,
|
|
161
|
+
competition,
|
|
162
|
+
season,
|
|
163
|
+
isRefreshing,
|
|
164
|
+
lastRefreshed,
|
|
165
|
+
matches,
|
|
166
|
+
standings,
|
|
167
|
+
previousSeasons,
|
|
168
|
+
config,
|
|
169
|
+
refresh,
|
|
170
|
+
matchHelpers,
|
|
171
|
+
standingsHelpers,
|
|
172
|
+
getTeamForm,
|
|
173
|
+
getTeamPosition,
|
|
174
|
+
standingsCalculations,
|
|
175
|
+
goalDistribution,
|
|
176
|
+
getGoalDistribution,
|
|
177
|
+
competitionStats,
|
|
178
|
+
getSeasonMatches,
|
|
179
|
+
getSeasonStandings,
|
|
180
|
+
getTeamResultsTable
|
|
181
|
+
]);
|
|
182
|
+
return /* @__PURE__ */ jsx(CompetitionContext.Provider, { value, children });
|
|
183
|
+
}
|
|
184
|
+
export {
|
|
185
|
+
CompetitionProvider
|
|
186
|
+
};
|
|
187
|
+
//# sourceMappingURL=competition.provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"competition.provider.js","sources":["../../src/lib/providers/competition.provider.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useMemo, useCallback } from \"react\";\nimport type { FUSportsMatch, FUSportsStandings, FUSportsCompetition, FUSportsSeasonDetails } from \"../types/canonical\";\nimport { CompetitionContext } from \"./competition.context\";\nimport type { CompetitionContextValue, CompetitionProviderProps } from \"./competition.types\";\nimport { getFootballCompetition } from \"../api/sportal365-sports/football/competitions\";\nimport { getFootballSeasonDetails } from \"../api/sportal365-sports/football/competitions\";\nimport { getFootballMatches } from \"../api/sportal365-sports/football/matches\";\nimport { getFootballStandings } from \"../api/sportal365-sports/football/standings\";\nimport { useMatchHelpers } from \"./competition/hooks/useMatchHelpers\";\nimport { useStandingsHelpers } from \"./competition/hooks/useStandingsHelpers\";\nimport { useStandingsCalculations } from \"./competition/hooks/useStandingsCalculations\";\nimport { useGoalDistribution } from \"./competition/hooks/useGoalDistribution\";\nimport { useCompetitionStats } from \"./competition/hooks/useCompetitionStats\";\nimport { useTeamResultsTable } from \"./competition/hooks/useTeamResultsTable\";\nimport { useTeamPosition } from \"./competition/hooks/useTeamPosition\";\nimport { useDataLayerConfig } from \"./fansunited-config.hooks\";\n\n// ============================================================================\n// Provider Component\n// ============================================================================\n\nexport function CompetitionProvider({\n competitionId,\n seasonId,\n stageId,\n competition: initialCompetition,\n season: initialSeason,\n matches: initialMatches,\n standings: initialStandings,\n previousSeasons,\n config,\n autoRefresh = true,\n refreshInterval = 30000,\n children,\n}: CompetitionProviderProps) {\n // Get data layer config from context\n const dataLayerConfig = useDataLayerConfig();\n\n // Core data state\n const [competition, setCompetition] = useState<FUSportsCompetition | undefined>(initialCompetition);\n const [season, setSeason] = useState<FUSportsSeasonDetails | undefined>(initialSeason);\n const [matches, setMatches] = useState<FUSportsMatch[]>(initialMatches ?? []);\n const [standings, setStandings] = useState<FUSportsStandings>(\n initialStandings ?? { entries: [], metadata: { legend: [] } }\n );\n\n // Refresh state\n const [isRefreshing, setIsRefreshing] = useState(false);\n const [lastRefreshed, setLastRefreshed] = useState<Date | undefined>(undefined);\n\n // DON'T update state from props - this causes infinite loops in Next.js\n // The initial values are set once, and only the refresh() function updates them\n\n // Initial fetch for competition and season (only once if not provided)\n useEffect(() => {\n if (competition && season) return;\n\n const fetchInitialData = async () => {\n try {\n const targetSeasonId = seasonId ?? season?.id;\n\n const [competitionData, seasonData] = await Promise.all([\n !competition\n ? getFootballCompetition(competitionId, undefined, dataLayerConfig)\n : Promise.resolve(competition),\n !season\n ? targetSeasonId\n ? getFootballSeasonDetails({ seasonId: targetSeasonId }, dataLayerConfig)\n : getFootballSeasonDetails({ competitionId, status: \"CURRENT\" }, dataLayerConfig)\n : Promise.resolve(season),\n ]);\n\n if (!competition) setCompetition(competitionData);\n if (!season) setSeason(seasonData);\n } catch (error) {\n console.error(\"[CompetitionProvider] Error fetching initial data:\", error);\n }\n };\n\n fetchInitialData();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [competitionId, seasonId, dataLayerConfig]);\n\n // Refresh function for matches and standings only\n const refresh = useCallback(async () => {\n const effectiveSeasonId = seasonId ?? season?.id;\n if (!effectiveSeasonId) return;\n\n setIsRefreshing(true);\n try {\n const effectiveStageId = stageId ?? season?.stages?.find((s) => s.status === \"ACTIVE\")?.id;\n\n const [matchesData, standingsData] = await Promise.all([\n getFootballMatches(\n {\n tournamentIds: [competitionId],\n seasonIds: [effectiveSeasonId],\n stageIds: effectiveStageId ? [effectiveStageId] : undefined,\n limit: 552,\n offset: 0,\n },\n dataLayerConfig\n ),\n effectiveStageId\n ? getFootballStandings(\n {\n stageId: effectiveStageId,\n expand: [\"rules\", \"form.events\"],\n },\n dataLayerConfig\n )\n : Promise.resolve({ entries: [], metadata: { legend: [] } }),\n ]);\n\n setMatches(matchesData);\n setStandings(standingsData);\n setLastRefreshed(new Date());\n } catch (error) {\n console.error(\"[CompetitionProvider] Error refreshing matches/standings:\", error);\n } finally {\n setIsRefreshing(false);\n }\n }, [competitionId, seasonId, stageId, season?.id, season?.stages, dataLayerConfig]);\n\n // Auto-refresh for live matches (matches and standings only)\n useEffect(() => {\n if (!autoRefresh) return;\n\n const hasLiveMatches = matches.some((m) => m.status.code === \"live\");\n if (!hasLiveMatches) return;\n\n const interval = setInterval(() => {\n refresh();\n }, refreshInterval);\n\n return () => clearInterval(interval);\n }, [autoRefresh, refreshInterval, matches, refresh]);\n\n // Use custom hooks for helper functions\n const matchHelpers = useMatchHelpers(matches);\n const standingsHelpers = useStandingsHelpers(standings);\n const standingsCalculations = useStandingsCalculations(matches, standings, standingsHelpers.getTeamStanding);\n const { goalDistribution, getGoalDistribution } = useGoalDistribution(matches);\n const competitionStats = useCompetitionStats(matches);\n const { getTeamResultsTable } = useTeamResultsTable(matches, standings);\n const { getTeamPosition } = useTeamPosition(standings);\n\n // Additional standings helpers\n const getTeamForm = useCallback(\n (teamId: string) => standingsHelpers.getTeamStanding(teamId)?.form,\n [standingsHelpers]\n );\n\n // Historical helpers\n const getSeasonMatches = useCallback(\n (seasonId: string) => previousSeasons?.find((s) => s.id === seasonId)?.matches,\n [previousSeasons]\n );\n\n const getSeasonStandings = useCallback(\n (seasonId: string) => previousSeasons?.find((s) => s.id === seasonId)?.standings,\n [previousSeasons]\n );\n\n // Memoize context value\n const value = useMemo<CompetitionContextValue>(() => {\n return {\n // Core identifiers\n competitionId,\n seasonId,\n stageId,\n\n // Competition and season info\n competition,\n season,\n\n // Loading and refresh state\n isRefreshing,\n lastRefreshed,\n\n // Core data\n matches,\n standings,\n\n // Historical data\n previousSeasons,\n\n // Custom configuration\n config,\n\n // Refresh method\n refresh,\n\n // Match helpers\n ...matchHelpers,\n\n // Standings helpers\n ...standingsHelpers,\n getTeamForm,\n getTeamPosition,\n\n // Form-based standings helpers\n ...standingsCalculations,\n\n // Goal distribution helpers\n goalDistribution,\n getGoalDistribution,\n\n // Competition statistics\n competitionStats,\n\n // Historical helpers\n getSeasonMatches,\n getSeasonStandings,\n\n // Team results table helper\n getTeamResultsTable,\n };\n }, [\n competitionId,\n seasonId,\n stageId,\n competition,\n season,\n isRefreshing,\n lastRefreshed,\n matches,\n standings,\n previousSeasons,\n config,\n refresh,\n matchHelpers,\n standingsHelpers,\n getTeamForm,\n getTeamPosition,\n standingsCalculations,\n goalDistribution,\n getGoalDistribution,\n competitionStats,\n getSeasonMatches,\n getSeasonStandings,\n getTeamResultsTable,\n ]);\n\n return <CompetitionContext.Provider value={value}>{children}</CompetitionContext.Provider>;\n}\n"],"names":["seasonId"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fansunited-config.context.js","sources":["../../src/lib/providers/fansunited-config.context.ts"],"sourcesContent":["import { createContext } from \"react\";\nimport type { FansUnitedConfig } from \"./fansunited-config.types\";\n\n/**\n * Context for FansUnited configuration\n */\nexport const FansUnitedConfigContext = createContext<FansUnitedConfig | null>(null);\n\n"],"names":[],"mappings":";AAMO,MAAM,0BAA0B,cAAuC,IAAI;"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { FansUnitedConfigContext } from "./fansunited-config.context.js";
|
|
3
|
+
function useFansUnitedConfig() {
|
|
4
|
+
const context = useContext(FansUnitedConfigContext);
|
|
5
|
+
if (!context) {
|
|
6
|
+
throw new Error(
|
|
7
|
+
"useFansUnitedConfig must be used within FansUnitedConfigProvider. Wrap your app with <FansUnitedConfigProvider config={...}>"
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
return context;
|
|
11
|
+
}
|
|
12
|
+
function useDataLayerConfig() {
|
|
13
|
+
const config = useFansUnitedConfig();
|
|
14
|
+
return config.dataLayer;
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
useDataLayerConfig,
|
|
18
|
+
useFansUnitedConfig
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=fansunited-config.hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fansunited-config.hooks.js","sources":["../../src/lib/providers/fansunited-config.hooks.ts"],"sourcesContent":["import { useContext } from \"react\";\nimport { FansUnitedConfigContext } from \"./fansunited-config.context\";\nimport type { FansUnitedConfig } from \"./fansunited-config.types\";\nimport type { DataLayerConfig } from \"../config\";\n\n/**\n * Hook to access the full FansUnited configuration\n * Must be used within a FansUnitedConfigProvider\n *\n * @returns The FansUnited configuration\n * @throws Error if used outside of FansUnitedConfigProvider\n */\nexport function useFansUnitedConfig(): FansUnitedConfig {\n const context = useContext(FansUnitedConfigContext);\n if (!context) {\n throw new Error(\n \"useFansUnitedConfig must be used within FansUnitedConfigProvider. \" +\n \"Wrap your app with <FansUnitedConfigProvider config={...}>\"\n );\n }\n return context;\n}\n\n/**\n * Hook to access the data layer configuration\n * Must be used within a FansUnitedConfigProvider\n *\n * @returns The data layer configuration\n * @throws Error if used outside of FansUnitedConfigProvider\n */\nexport function useDataLayerConfig(): DataLayerConfig {\n const config = useFansUnitedConfig();\n return config.dataLayer;\n}\n"],"names":[],"mappings":";;AAYO,SAAS,sBAAwC;AACpD,QAAM,UAAU,WAAW,uBAAuB;AAClD,MAAI,CAAC,SAAS;AACV,UAAM,IAAI;AAAA,MACN;AAAA,IAAA;AAAA,EAGR;AACA,SAAO;AACX;AASO,SAAS,qBAAsC;AAClD,QAAM,SAAS,oBAAA;AACf,SAAO,OAAO;AAClB;"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { FansUnitedConfigContext } from "./fansunited-config.context.js";
|
|
3
|
+
function FansUnitedConfigProvider({ config, children }) {
|
|
4
|
+
return /* @__PURE__ */ jsx(FansUnitedConfigContext.Provider, { value: config, children });
|
|
5
|
+
}
|
|
6
|
+
export {
|
|
7
|
+
FansUnitedConfigProvider
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=fansunited-config.provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fansunited-config.provider.js","sources":["../../src/lib/providers/fansunited-config.provider.tsx"],"sourcesContent":["\"use client\";\n\nimport { FansUnitedConfigContext } from \"./fansunited-config.context\";\nimport type { FansUnitedConfigProviderProps } from \"./fansunited-config.types\";\n\n/**\n * FansUnitedConfigProvider - Provides configuration to all FansUnited components\n *\n * This provider should wrap your app at the root level to provide configuration\n * to all data layer providers (TeamProvider, CompetitionProvider, etc.).\n *\n * In Next.js App Router, add this to your root layout:\n *\n * @example\n * ```typescript\n * // app/layout.tsx (Server Component)\n * import { setConfig } from 'fansunited-data-layer';\n * import { FansUnitedConfigProvider } from 'fansunited-data-layer/client';\n *\n * const config = {\n * dataLayer: {\n * defaultSportsProvider: 'sportal365Sports',\n * sportal365Sports: {\n * username: process.env.SPORTAL365_USERNAME!,\n * password: process.env.SPORTAL365_PASSWORD!,\n * projectId: process.env.SPORTAL365_PROJECT_ID!,\n * }\n * }\n * };\n *\n * // Initialize server-side config\n * setConfig(config.dataLayer);\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <body>\n * <FansUnitedConfigProvider config={config}>\n * {children}\n * </FansUnitedConfigProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport function FansUnitedConfigProvider({ config, children }: FansUnitedConfigProviderProps) {\n return <FansUnitedConfigContext.Provider value={config}>{children}</FansUnitedConfigContext.Provider>;\n}\n"],"names":[],"mappings":";;AA8CO,SAAS,yBAAyB,EAAE,QAAQ,YAA2C;AAC1F,6BAAQ,wBAAwB,UAAxB,EAAiC,OAAO,QAAS,UAAS;AACtE;"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { FansUnitedSDK } from "fansunited-sdk-esm";
|
|
3
|
+
import { useFansUnitedConfig } from "./fansunited-config.hooks.js";
|
|
4
|
+
function useFansUnitedSDK() {
|
|
5
|
+
const config = useFansUnitedConfig();
|
|
6
|
+
const sdkInstance = useMemo(() => {
|
|
7
|
+
if (typeof window === "undefined") {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
if (!config.sdk) {
|
|
11
|
+
console.error(
|
|
12
|
+
"FansUnited SDK configuration not provided. Add 'sdk' property to your FansUnitedConfigProvider config."
|
|
13
|
+
);
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const sdkConfig = config.sdk;
|
|
17
|
+
if (!sdkConfig.apiKey || !sdkConfig.clientId) {
|
|
18
|
+
console.error("FansUnited SDK configuration is invalid. Both 'apiKey' and 'clientId' are required.");
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
if (!sdkConfig.authProvider && !sdkConfig.cookieToken) {
|
|
22
|
+
console.error(
|
|
23
|
+
"FansUnited SDK configuration is invalid. Either 'authProvider' or 'cookieToken' must be provided."
|
|
24
|
+
);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const sdkConfigModel = {
|
|
29
|
+
apiKey: sdkConfig.apiKey,
|
|
30
|
+
clientId: sdkConfig.clientId,
|
|
31
|
+
environment: sdkConfig.environment || "prod",
|
|
32
|
+
lang: sdkConfig.lang || "en",
|
|
33
|
+
idSchema: sdkConfig.idSchema,
|
|
34
|
+
errorHandlingMode: sdkConfig.errorHandlingMode,
|
|
35
|
+
oddClient: sdkConfig.oddClient,
|
|
36
|
+
cookieToken: sdkConfig.cookieToken,
|
|
37
|
+
authProvider: sdkConfig.authProvider
|
|
38
|
+
};
|
|
39
|
+
return FansUnitedSDK(sdkConfigModel);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error("Failed to initialize FansUnited SDK:", error);
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}, [config.sdk]);
|
|
45
|
+
return sdkInstance;
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
useFansUnitedSDK
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=fansunited-sdk.hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fansunited-sdk.hook.js","sources":["../../src/lib/providers/fansunited-sdk.hook.ts"],"sourcesContent":["\"use client\";\n\nimport { useMemo } from \"react\";\nimport { FansUnitedSDK } from \"fansunited-sdk-esm\";\nimport type FansUnitedSDKModel from \"fansunited-sdk-esm/Core/Global/Models/FansUnitedSDKModel\";\nimport type SDKConfigurationModel from \"fansunited-sdk-esm/Core/Configurator/Models/SDKConfiguraitonModel\";\nimport { useFansUnitedConfig } from \"./fansunited-config.hooks\";\n\n/**\n * Hook to access the FansUnited SDK instance\n *\n * This hook initializes and returns a shared SDK instance based on the configuration\n * provided to FansUnitedConfigProvider. The SDK instance is memoized and shared\n * across all components that use this hook.\n *\n * **Client-side only**: Returns null on server-side rendering.\n *\n * @returns The FansUnited SDK instance or null if:\n * - Running on server-side\n * - SDK configuration is not provided\n * - SDK configuration is invalid\n *\n * @example\n * ```typescript\n * \"use client\";\n *\n * import { useFansUnitedSDK } from 'fansunited-data-layer/client';\n *\n * function MyComponent() {\n * const sdk = useFansUnitedSDK();\n *\n * if (!sdk) {\n * return <div>SDK not available</div>;\n * }\n *\n * // Use the SDK\n * const handleGetProfile = async () => {\n * const profile = await sdk.profile.getOwn().getInfo();\n * console.log(profile);\n * };\n *\n * return <button onClick={handleGetProfile}>Get Profile</button>;\n * }\n * ```\n */\nexport function useFansUnitedSDK(): FansUnitedSDKModel | null {\n const config = useFansUnitedConfig();\n\n const sdkInstance = useMemo(() => {\n // Check if we're on the client side\n if (typeof window === \"undefined\") {\n return null;\n }\n\n // Check if SDK config is provided\n if (!config.sdk) {\n console.error(\n \"FansUnited SDK configuration not provided. \" +\n \"Add 'sdk' property to your FansUnitedConfigProvider config.\"\n );\n return null;\n }\n\n const sdkConfig = config.sdk;\n\n // Validate required fields\n if (!sdkConfig.apiKey || !sdkConfig.clientId) {\n console.error(\"FansUnited SDK configuration is invalid. Both 'apiKey' and 'clientId' are required.\");\n return null;\n }\n\n // Validate that at least one authentication method is provided\n if (!sdkConfig.authProvider && !sdkConfig.cookieToken) {\n console.error(\n \"FansUnited SDK configuration is invalid. Either 'authProvider' or 'cookieToken' must be provided.\"\n );\n return null;\n }\n\n try {\n // Build SDK configuration with defaults\n const sdkConfigModel: SDKConfigurationModel = {\n apiKey: sdkConfig.apiKey,\n clientId: sdkConfig.clientId,\n environment: sdkConfig.environment || \"prod\",\n lang: sdkConfig.lang || \"en\",\n idSchema: sdkConfig.idSchema,\n errorHandlingMode: sdkConfig.errorHandlingMode,\n oddClient: sdkConfig.oddClient,\n cookieToken: sdkConfig.cookieToken,\n authProvider: sdkConfig.authProvider,\n };\n\n // Initialize and return SDK instance\n return FansUnitedSDK(sdkConfigModel);\n } catch (error) {\n console.error(\"Failed to initialize FansUnited SDK:\", error);\n return null;\n }\n }, [config.sdk]);\n\n return sdkInstance;\n}\n"],"names":[],"mappings":";;;AA6CO,SAAS,mBAA8C;AAC1D,QAAM,SAAS,oBAAA;AAEf,QAAM,cAAc,QAAQ,MAAM;AAE9B,QAAI,OAAO,WAAW,aAAa;AAC/B,aAAO;AAAA,IACX;AAGA,QAAI,CAAC,OAAO,KAAK;AACb,cAAQ;AAAA,QACJ;AAAA,MAAA;AAGJ,aAAO;AAAA,IACX;AAEA,UAAM,YAAY,OAAO;AAGzB,QAAI,CAAC,UAAU,UAAU,CAAC,UAAU,UAAU;AAC1C,cAAQ,MAAM,qFAAqF;AACnG,aAAO;AAAA,IACX;AAGA,QAAI,CAAC,UAAU,gBAAgB,CAAC,UAAU,aAAa;AACnD,cAAQ;AAAA,QACJ;AAAA,MAAA;AAEJ,aAAO;AAAA,IACX;AAEA,QAAI;AAEA,YAAM,iBAAwC;AAAA,QAC1C,QAAQ,UAAU;AAAA,QAClB,UAAU,UAAU;AAAA,QACpB,aAAa,UAAU,eAAe;AAAA,QACtC,MAAM,UAAU,QAAQ;AAAA,QACxB,UAAU,UAAU;AAAA,QACpB,mBAAmB,UAAU;AAAA,QAC7B,WAAW,UAAU;AAAA,QACrB,aAAa,UAAU;AAAA,QACvB,cAAc,UAAU;AAAA,MAAA;AAI5B,aAAO,cAAc,cAAc;AAAA,IACvC,SAAS,OAAO;AACZ,cAAQ,MAAM,wCAAwC,KAAK;AAC3D,aAAO;AAAA,IACX;AAAA,EACJ,GAAG,CAAC,OAAO,GAAG,CAAC;AAEf,SAAO;AACX;"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useCallback, useMemo } from "react";
|
|
2
|
+
function useMatchHelpers(matches) {
|
|
3
|
+
const getMatchById = useCallback((id) => matches.find((m) => m.id === id), [matches]);
|
|
4
|
+
const getTeamMatches = useCallback(
|
|
5
|
+
(teamId) => matches.filter((m) => m.competitorOne.id === teamId || m.competitorTwo.id === teamId),
|
|
6
|
+
[matches]
|
|
7
|
+
);
|
|
8
|
+
const getLiveMatches = useCallback(() => matches.filter((m) => m.status.code === "live"), [matches]);
|
|
9
|
+
const getUpcomingMatches = useCallback(() => {
|
|
10
|
+
const now = /* @__PURE__ */ new Date();
|
|
11
|
+
return matches.filter((m) => m.status.code === "not_started" && m.timing.kickoffTime > now).sort((a, b) => a.timing.kickoffTime.getTime() - b.timing.kickoffTime.getTime());
|
|
12
|
+
}, [matches]);
|
|
13
|
+
const getFinishedMatches = useCallback(() => {
|
|
14
|
+
return matches.filter((m) => m.status.type === "FINISHED").sort((a, b) => b.timing.kickoffTime.getTime() - a.timing.kickoffTime.getTime());
|
|
15
|
+
}, [matches]);
|
|
16
|
+
return useMemo(
|
|
17
|
+
() => ({
|
|
18
|
+
getMatchById,
|
|
19
|
+
getTeamMatches,
|
|
20
|
+
getLiveMatches,
|
|
21
|
+
getUpcomingMatches,
|
|
22
|
+
getFinishedMatches
|
|
23
|
+
}),
|
|
24
|
+
[getMatchById, getTeamMatches, getLiveMatches, getUpcomingMatches, getFinishedMatches]
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
useMatchHelpers
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=useMatchHelpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMatchHelpers.js","sources":["../../../../src/lib/providers/leaderboard/hooks/useMatchHelpers.ts"],"sourcesContent":["import { useCallback, useMemo } from \"react\";\nimport type { FUSportsMatch } from \"../../../types/canonical\";\n\n/**\n * Hook for leaderboard match-related helper functions\n */\nexport function useMatchHelpers(matches: FUSportsMatch[]) {\n const getMatchById = useCallback((id: string) => matches.find((m) => m.id === id), [matches]);\n\n const getTeamMatches = useCallback(\n (teamId: string) => matches.filter((m) => m.competitorOne.id === teamId || m.competitorTwo.id === teamId),\n [matches]\n );\n\n const getLiveMatches = useCallback(() => matches.filter((m) => m.status.code === \"live\"), [matches]);\n\n const getUpcomingMatches = useCallback(() => {\n const now = new Date();\n return matches\n .filter((m) => m.status.code === \"not_started\" && m.timing.kickoffTime > now)\n .sort((a, b) => a.timing.kickoffTime.getTime() - b.timing.kickoffTime.getTime());\n }, [matches]);\n\n const getFinishedMatches = useCallback(() => {\n return matches\n .filter((m) => m.status.type === \"FINISHED\")\n .sort((a, b) => b.timing.kickoffTime.getTime() - a.timing.kickoffTime.getTime());\n }, [matches]);\n\n return useMemo(\n () => ({\n getMatchById,\n getTeamMatches,\n getLiveMatches,\n getUpcomingMatches,\n getFinishedMatches,\n }),\n [getMatchById, getTeamMatches, getLiveMatches, getUpcomingMatches, getFinishedMatches]\n );\n}\n\n"],"names":[],"mappings":";AAMO,SAAS,gBAAgB,SAA0B;AACtD,QAAM,eAAe,YAAY,CAAC,OAAe,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC;AAE5F,QAAM,iBAAiB;AAAA,IACnB,CAAC,WAAmB,QAAQ,OAAO,CAAC,MAAM,EAAE,cAAc,OAAO,UAAU,EAAE,cAAc,OAAO,MAAM;AAAA,IACxG,CAAC,OAAO;AAAA,EAAA;AAGZ,QAAM,iBAAiB,YAAY,MAAM,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,SAAS,MAAM,GAAG,CAAC,OAAO,CAAC;AAEnG,QAAM,qBAAqB,YAAY,MAAM;AACzC,UAAM,0BAAU,KAAA;AAChB,WAAO,QACF,OAAO,CAAC,MAAM,EAAE,OAAO,SAAS,iBAAiB,EAAE,OAAO,cAAc,GAAG,EAC3E,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,YAAY,QAAA,IAAY,EAAE,OAAO,YAAY,QAAA,CAAS;AAAA,EACvF,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,qBAAqB,YAAY,MAAM;AACzC,WAAO,QACF,OAAO,CAAC,MAAM,EAAE,OAAO,SAAS,UAAU,EAC1C,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,YAAY,YAAY,EAAE,OAAO,YAAY,SAAS;AAAA,EACvF,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO;AAAA,IACH,OAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAEJ,CAAC,cAAc,gBAAgB,gBAAgB,oBAAoB,kBAAkB;AAAA,EAAA;AAE7F;"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useCallback, useMemo } from "react";
|
|
2
|
+
function useTemplateHelpers(groups, matches) {
|
|
3
|
+
const getGroupMatchIds = useCallback(
|
|
4
|
+
(groupId) => {
|
|
5
|
+
const group = groups.find((g) => g.groupId === groupId);
|
|
6
|
+
return group?.filters?.matchIds || [];
|
|
7
|
+
},
|
|
8
|
+
[groups]
|
|
9
|
+
);
|
|
10
|
+
const getAllMatchIds = useCallback(() => {
|
|
11
|
+
const allIds = /* @__PURE__ */ new Set();
|
|
12
|
+
for (const group of groups) {
|
|
13
|
+
const matchIds = group?.filters?.matchIds || [];
|
|
14
|
+
for (const id of matchIds) {
|
|
15
|
+
allIds.add(id);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return Array.from(allIds);
|
|
19
|
+
}, [groups]);
|
|
20
|
+
const getGroupMatches = useCallback(
|
|
21
|
+
(groupId) => {
|
|
22
|
+
const matchIds = getGroupMatchIds(groupId);
|
|
23
|
+
return matches.filter((m) => matchIds.includes(m.id));
|
|
24
|
+
},
|
|
25
|
+
[getGroupMatchIds, matches]
|
|
26
|
+
);
|
|
27
|
+
return useMemo(
|
|
28
|
+
() => ({
|
|
29
|
+
getGroupMatchIds,
|
|
30
|
+
getAllMatchIds,
|
|
31
|
+
getGroupMatches
|
|
32
|
+
}),
|
|
33
|
+
[getGroupMatchIds, getAllMatchIds, getGroupMatches]
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
useTemplateHelpers
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=useTemplateHelpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTemplateHelpers.js","sources":["../../../../src/lib/providers/leaderboard/hooks/useTemplateHelpers.ts"],"sourcesContent":["import { useCallback, useMemo } from \"react\";\nimport type { FUSportsMatch } from \"../../../types/canonical\";\nimport type { SerializedTemplateGroup } from \"../../leaderboard.types\";\n\n/**\n * Hook for template/group-related helper functions\n */\nexport function useTemplateHelpers(groups: SerializedTemplateGroup[], matches: FUSportsMatch[]) {\n const getGroupMatchIds = useCallback(\n (groupId: string): string[] => {\n const group = groups.find((g) => g.groupId === groupId);\n return group?.filters?.matchIds || [];\n },\n [groups]\n );\n\n const getAllMatchIds = useCallback((): string[] => {\n const allIds = new Set<string>();\n for (const group of groups) {\n const matchIds = group?.filters?.matchIds || [];\n for (const id of matchIds) {\n allIds.add(id);\n }\n }\n return Array.from(allIds);\n }, [groups]);\n\n const getGroupMatches = useCallback(\n (groupId: string): FUSportsMatch[] => {\n const matchIds = getGroupMatchIds(groupId);\n return matches.filter((m) => matchIds.includes(m.id));\n },\n [getGroupMatchIds, matches]\n );\n\n return useMemo(\n () => ({\n getGroupMatchIds,\n getAllMatchIds,\n getGroupMatches,\n }),\n [getGroupMatchIds, getAllMatchIds, getGroupMatches]\n );\n}\n\n"],"names":[],"mappings":";AAOO,SAAS,mBAAmB,QAAmC,SAA0B;AAC5F,QAAM,mBAAmB;AAAA,IACrB,CAAC,YAA8B;AAC3B,YAAM,QAAQ,OAAO,KAAK,CAAC,MAAM,EAAE,YAAY,OAAO;AACtD,aAAO,OAAO,SAAS,YAAY,CAAA;AAAA,IACvC;AAAA,IACA,CAAC,MAAM;AAAA,EAAA;AAGX,QAAM,iBAAiB,YAAY,MAAgB;AAC/C,UAAM,6BAAa,IAAA;AACnB,eAAW,SAAS,QAAQ;AACxB,YAAM,WAAW,OAAO,SAAS,YAAY,CAAA;AAC7C,iBAAW,MAAM,UAAU;AACvB,eAAO,IAAI,EAAE;AAAA,MACjB;AAAA,IACJ;AACA,WAAO,MAAM,KAAK,MAAM;AAAA,EAC5B,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,kBAAkB;AAAA,IACpB,CAAC,YAAqC;AAClC,YAAM,WAAW,iBAAiB,OAAO;AACzC,aAAO,QAAQ,OAAO,CAAC,MAAM,SAAS,SAAS,EAAE,EAAE,CAAC;AAAA,IACxD;AAAA,IACA,CAAC,kBAAkB,OAAO;AAAA,EAAA;AAG9B,SAAO;AAAA,IACH,OAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAEJ,CAAC,kBAAkB,gBAAgB,eAAe;AAAA,EAAA;AAE1D;"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
const LeaderboardContext = createContext(null);
|
|
3
|
+
function useLeaderboard() {
|
|
4
|
+
const context = useContext(LeaderboardContext);
|
|
5
|
+
if (!context) {
|
|
6
|
+
throw new Error("useLeaderboard must be used within LeaderboardProvider");
|
|
7
|
+
}
|
|
8
|
+
return context;
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
LeaderboardContext,
|
|
12
|
+
useLeaderboard
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=leaderboard.context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leaderboard.context.js","sources":["../../src/lib/providers/leaderboard.context.ts"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { LeaderboardContextValue } from \"./leaderboard.types\";\n\n// ============================================================================\n// Context\n// ============================================================================\n\nexport const LeaderboardContext = createContext<LeaderboardContextValue | null>(null);\n\n// ============================================================================\n// Hook\n// ============================================================================\n\n/**\n * Hook to access leaderboard context\n * Must be used within a LeaderboardProvider\n */\nexport function useLeaderboard(): LeaderboardContextValue {\n const context = useContext(LeaderboardContext);\n if (!context) {\n throw new Error(\"useLeaderboard must be used within LeaderboardProvider\");\n }\n return context;\n}\n\n"],"names":[],"mappings":";AASO,MAAM,qBAAqB,cAA8C,IAAI;AAU7E,SAAS,iBAA0C;AACtD,QAAM,UAAU,WAAW,kBAAkB;AAC7C,MAAI,CAAC,SAAS;AACV,UAAM,IAAI,MAAM,wDAAwD;AAAA,EAC5E;AACA,SAAO;AACX;"}
|