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.
Files changed (242) hide show
  1. package/api/fansunited/constants.js +5 -0
  2. package/api/fansunited/constants.js.map +1 -0
  3. package/api/fansunited/football/competition/index.js +21 -0
  4. package/api/fansunited/football/competition/index.js.map +1 -0
  5. package/api/fansunited/football/competition/transformer.js +41 -0
  6. package/api/fansunited/football/competition/transformer.js.map +1 -0
  7. package/api/fansunited/football/competitions/index.js +56 -0
  8. package/api/fansunited/football/competitions/index.js.map +1 -0
  9. package/api/fansunited/football/competitions/transformer.js +68 -0
  10. package/api/fansunited/football/competitions/transformer.js.map +1 -0
  11. package/api/fansunited/football/http.js +7 -0
  12. package/api/fansunited/football/http.js.map +1 -0
  13. package/api/fansunited/football/matches/index.js +132 -0
  14. package/api/fansunited/football/matches/index.js.map +1 -0
  15. package/api/fansunited/football/matches/transformer.js +148 -0
  16. package/api/fansunited/football/matches/transformer.js.map +1 -0
  17. package/api/fansunited/football/players/index.js +60 -0
  18. package/api/fansunited/football/players/index.js.map +1 -0
  19. package/api/fansunited/football/players/transformer.js +85 -0
  20. package/api/fansunited/football/players/transformer.js.map +1 -0
  21. package/api/fansunited/football/search/index.js +64 -0
  22. package/api/fansunited/football/search/index.js.map +1 -0
  23. package/api/fansunited/football/search/transformer.js +114 -0
  24. package/api/fansunited/football/search/transformer.js.map +1 -0
  25. package/api/fansunited/football/teams/index.js +66 -0
  26. package/api/fansunited/football/teams/index.js.map +1 -0
  27. package/api/fansunited/football/teams/transformer.js +53 -0
  28. package/api/fansunited/football/teams/transformer.js.map +1 -0
  29. package/api/fansunited/http.d.ts.map +1 -1
  30. package/api/fansunited/http.js +70 -0
  31. package/api/fansunited/http.js.map +1 -0
  32. package/api/fansunited/search/constants.js +5 -0
  33. package/api/fansunited/search/constants.js.map +1 -0
  34. package/api/fansunited/search/http.js +7 -0
  35. package/api/fansunited/search/http.js.map +1 -0
  36. package/api/fansunited/search/index.js +308 -0
  37. package/api/fansunited/search/index.js.map +1 -0
  38. package/api/fansunited/search/transformer.js +374 -0
  39. package/api/fansunited/search/transformer.js.map +1 -0
  40. package/api/fansunited-sdk/loyalty/matches.js +21 -0
  41. package/api/fansunited-sdk/loyalty/matches.js.map +1 -0
  42. package/api/fansunited-sdk/loyalty/template.js +52 -0
  43. package/api/fansunited-sdk/loyalty/template.js.map +1 -0
  44. package/api/fansunited-sdk/odds/matches.js +109 -0
  45. package/api/fansunited-sdk/odds/matches.js.map +1 -0
  46. package/api/sportal365-sports/basketball/games/index.js +32 -0
  47. package/api/sportal365-sports/basketball/games/index.js.map +1 -0
  48. package/api/sportal365-sports/basketball/games/transformers/game.js +137 -0
  49. package/api/sportal365-sports/basketball/games/transformers/game.js.map +1 -0
  50. package/api/sportal365-sports/basketball/http.js +7 -0
  51. package/api/sportal365-sports/basketball/http.js.map +1 -0
  52. package/api/sportal365-sports/constants.js +15 -0
  53. package/api/sportal365-sports/constants.js.map +1 -0
  54. package/api/sportal365-sports/football/competitions/index.js +52 -0
  55. package/api/sportal365-sports/football/competitions/index.js.map +1 -0
  56. package/api/sportal365-sports/football/competitions/utils.js +92 -0
  57. package/api/sportal365-sports/football/competitions/utils.js.map +1 -0
  58. package/api/sportal365-sports/football/http.js +7 -0
  59. package/api/sportal365-sports/football/http.js.map +1 -0
  60. package/api/sportal365-sports/football/matches/index.js +206 -0
  61. package/api/sportal365-sports/football/matches/index.js.map +1 -0
  62. package/api/sportal365-sports/football/matches/transformers/commentary.js +27 -0
  63. package/api/sportal365-sports/football/matches/transformers/commentary.js.map +1 -0
  64. package/api/sportal365-sports/football/matches/transformers/lineup.js +74 -0
  65. package/api/sportal365-sports/football/matches/transformers/lineup.js.map +1 -0
  66. package/api/sportal365-sports/football/matches/transformers/match-event.js +60 -0
  67. package/api/sportal365-sports/football/matches/transformers/match-event.js.map +1 -0
  68. package/api/sportal365-sports/football/matches/transformers/match.js +195 -0
  69. package/api/sportal365-sports/football/matches/transformers/match.js.map +1 -0
  70. package/api/sportal365-sports/football/matches/transformers/odds.js +11 -0
  71. package/api/sportal365-sports/football/matches/transformers/odds.js.map +1 -0
  72. package/api/sportal365-sports/football/matches/transformers/statistics.js +41 -0
  73. package/api/sportal365-sports/football/matches/transformers/statistics.js.map +1 -0
  74. package/api/sportal365-sports/football/search/index.js +42 -0
  75. package/api/sportal365-sports/football/search/index.js.map +1 -0
  76. package/api/sportal365-sports/football/search/search.transformer.js +166 -0
  77. package/api/sportal365-sports/football/search/search.transformer.js.map +1 -0
  78. package/api/sportal365-sports/football/standings/index.js +28 -0
  79. package/api/sportal365-sports/football/standings/index.js.map +1 -0
  80. package/api/sportal365-sports/football/standings/standing.transformer.js +107 -0
  81. package/api/sportal365-sports/football/standings/standing.transformer.js.map +1 -0
  82. package/api/sportal365-sports/football/statistics/index.js +154 -0
  83. package/api/sportal365-sports/football/statistics/index.js.map +1 -0
  84. package/api/sportal365-sports/football/statistics/player-career.transformer.js +77 -0
  85. package/api/sportal365-sports/football/statistics/player-career.transformer.js.map +1 -0
  86. package/api/sportal365-sports/football/statistics/player-recent.transformer.js +54 -0
  87. package/api/sportal365-sports/football/statistics/player-recent.transformer.js.map +1 -0
  88. package/api/sportal365-sports/football/statistics/player-season.transformer.js +147 -0
  89. package/api/sportal365-sports/football/statistics/player-season.transformer.js.map +1 -0
  90. package/api/sportal365-sports/football/teams/index.js +73 -0
  91. package/api/sportal365-sports/football/teams/index.js.map +1 -0
  92. package/api/sportal365-sports/football/teams/utils.js +196 -0
  93. package/api/sportal365-sports/football/teams/utils.js.map +1 -0
  94. package/api/sportal365-sports/http.js +58 -0
  95. package/api/sportal365-sports/http.js.map +1 -0
  96. package/api/sportal365-sports/search/http.js +7 -0
  97. package/api/sportal365-sports/search/http.js.map +1 -0
  98. package/api/sportal365-sports/search/index.js +61 -0
  99. package/api/sportal365-sports/search/index.js.map +1 -0
  100. package/api/sportal365-sports/search/search.transformer.js +254 -0
  101. package/api/sportal365-sports/search/search.transformer.js.map +1 -0
  102. package/api/sportal365-sports/shared/odds.transformer.js +65 -0
  103. package/api/sportal365-sports/shared/odds.transformer.js.map +1 -0
  104. package/api/sportal365-sports/shared/providerRef.helper.js +31 -0
  105. package/api/sportal365-sports/shared/providerRef.helper.js.map +1 -0
  106. package/api/sportal365-sports/standings/http.js +7 -0
  107. package/api/sportal365-sports/standings/http.js.map +1 -0
  108. package/api/sportal365-sports/standings/index.js +29 -0
  109. package/api/sportal365-sports/standings/index.js.map +1 -0
  110. package/api/sportal365-sports/standings/standing.transformer.js +185 -0
  111. package/api/sportal365-sports/standings/standing.transformer.js.map +1 -0
  112. package/api/sportal365-sports/statistics/http.js +7 -0
  113. package/api/sportal365-sports/statistics/http.js.map +1 -0
  114. package/api/sportal365-sports/statistics/index.js +25 -0
  115. package/api/sportal365-sports/statistics/index.js.map +1 -0
  116. package/api/sportal365-sports/statistics/team-stats.transformer.js +16 -0
  117. package/api/sportal365-sports/statistics/team-stats.transformer.js.map +1 -0
  118. package/api/sportal365-sports/tennis/http.js +7 -0
  119. package/api/sportal365-sports/tennis/http.js.map +1 -0
  120. package/api/sportal365-sports/tennis/matches/index.js +34 -0
  121. package/api/sportal365-sports/tennis/matches/index.js.map +1 -0
  122. package/api/sportal365-sports/tennis/matches/transformers/match.js +193 -0
  123. package/api/sportal365-sports/tennis/matches/transformers/match.js.map +1 -0
  124. package/cache/cache-manager.js +136 -0
  125. package/cache/cache-manager.js.map +1 -0
  126. package/cache/memory-store.js +29 -0
  127. package/cache/memory-store.js.map +1 -0
  128. package/client.js +38 -12337
  129. package/client.js.map +1 -0
  130. package/config/index.js +20 -0
  131. package/config/index.js.map +1 -0
  132. package/fansunited-data-layer.js +146 -3553
  133. package/fansunited-data-layer.js.map +1 -0
  134. package/helpers/competition.helpers.js +11 -0
  135. package/helpers/competition.helpers.js.map +1 -0
  136. package/helpers/player.helpers.js +165 -0
  137. package/helpers/player.helpers.js.map +1 -0
  138. package/helpers/player.hooks.js +34 -0
  139. package/helpers/player.hooks.js.map +1 -0
  140. package/helpers/team.helpers.js +216 -0
  141. package/helpers/team.helpers.js.map +1 -0
  142. package/package.json +6 -8
  143. package/providers/competition/hooks/useCompetitionStats.js +10 -0
  144. package/providers/competition/hooks/useCompetitionStats.js.map +1 -0
  145. package/providers/competition/hooks/useGoalDistribution.js +23 -0
  146. package/providers/competition/hooks/useGoalDistribution.js.map +1 -0
  147. package/providers/competition/hooks/useMatchHelpers.js +85 -0
  148. package/providers/competition/hooks/useMatchHelpers.js.map +1 -0
  149. package/providers/competition/hooks/useStandingsCalculations.js +89 -0
  150. package/providers/competition/hooks/useStandingsCalculations.js.map +1 -0
  151. package/providers/competition/hooks/useStandingsHelpers.js +61 -0
  152. package/providers/competition/hooks/useStandingsHelpers.js.map +1 -0
  153. package/providers/competition/hooks/useTeamPosition.js +15 -0
  154. package/providers/competition/hooks/useTeamPosition.js.map +1 -0
  155. package/providers/competition/hooks/useTeamResultsTable.js +86 -0
  156. package/providers/competition/hooks/useTeamResultsTable.js.map +1 -0
  157. package/providers/competition/utils/competitionStats.js +109 -0
  158. package/providers/competition/utils/competitionStats.js.map +1 -0
  159. package/providers/competition/utils/goalDistribution.js +64 -0
  160. package/providers/competition/utils/goalDistribution.js.map +1 -0
  161. package/providers/competition/utils/standingsCalculations.js +152 -0
  162. package/providers/competition/utils/standingsCalculations.js.map +1 -0
  163. package/providers/competition.context.js +14 -0
  164. package/providers/competition.context.js.map +1 -0
  165. package/providers/competition.provider.js +187 -0
  166. package/providers/competition.provider.js.map +1 -0
  167. package/providers/fansunited-config.context.js +6 -0
  168. package/providers/fansunited-config.context.js.map +1 -0
  169. package/providers/fansunited-config.hooks.js +20 -0
  170. package/providers/fansunited-config.hooks.js.map +1 -0
  171. package/providers/fansunited-config.provider.js +9 -0
  172. package/providers/fansunited-config.provider.js.map +1 -0
  173. package/providers/fansunited-sdk.hook.js +50 -0
  174. package/providers/fansunited-sdk.hook.js.map +1 -0
  175. package/providers/leaderboard/hooks/useMatchHelpers.js +30 -0
  176. package/providers/leaderboard/hooks/useMatchHelpers.js.map +1 -0
  177. package/providers/leaderboard/hooks/useTemplateHelpers.js +39 -0
  178. package/providers/leaderboard/hooks/useTemplateHelpers.js.map +1 -0
  179. package/providers/leaderboard.context.js +14 -0
  180. package/providers/leaderboard.context.js.map +1 -0
  181. package/providers/leaderboard.provider.js +98 -0
  182. package/providers/leaderboard.provider.js.map +1 -0
  183. package/providers/match/hooks/useBatchMatchOdds.js +72 -0
  184. package/providers/match/hooks/useBatchMatchOdds.js.map +1 -0
  185. package/providers/match/hooks/useEventHelpers.js +46 -0
  186. package/providers/match/hooks/useEventHelpers.js.map +1 -0
  187. package/providers/match/hooks/useHeadToHeadHelpers.js +52 -0
  188. package/providers/match/hooks/useHeadToHeadHelpers.js.map +1 -0
  189. package/providers/match/hooks/useLineupHelpers.js +53 -0
  190. package/providers/match/hooks/useLineupHelpers.js.map +1 -0
  191. package/providers/match/hooks/useMatchStandingsCalculations.js +332 -0
  192. package/providers/match/hooks/useMatchStandingsCalculations.js.map +1 -0
  193. package/providers/match/hooks/useMatchStatus.js +37 -0
  194. package/providers/match/hooks/useMatchStatus.js.map +1 -0
  195. package/providers/match/hooks/useOddsHelpers.js +54 -0
  196. package/providers/match/hooks/useOddsHelpers.js.map +1 -0
  197. package/providers/match/hooks/useScoreHelpers.js +34 -0
  198. package/providers/match/hooks/useScoreHelpers.js.map +1 -0
  199. package/providers/match/hooks/useStandingsHelpers.js +53 -0
  200. package/providers/match/hooks/useStandingsHelpers.js.map +1 -0
  201. package/providers/match/hooks/useStatisticsHelpers.js +40 -0
  202. package/providers/match/hooks/useStatisticsHelpers.js.map +1 -0
  203. package/providers/match/hooks/useTeamHelpers.js +38 -0
  204. package/providers/match/hooks/useTeamHelpers.js.map +1 -0
  205. package/providers/match/hooks/useTeamStatsHelpers.js +141 -0
  206. package/providers/match/hooks/useTeamStatsHelpers.js.map +1 -0
  207. package/providers/match.context.js +14 -0
  208. package/providers/match.context.js.map +1 -0
  209. package/providers/match.provider.js +176 -0
  210. package/providers/match.provider.js.map +1 -0
  211. package/providers/team/hooks/useFormStats.js +116 -0
  212. package/providers/team/hooks/useFormStats.js.map +1 -0
  213. package/providers/team/hooks/useMatchHelpers.js +46 -0
  214. package/providers/team/hooks/useMatchHelpers.js.map +1 -0
  215. package/providers/team/hooks/useSquadHelpers.js +67 -0
  216. package/providers/team/hooks/useSquadHelpers.js.map +1 -0
  217. package/providers/team.context.js +14 -0
  218. package/providers/team.context.js.map +1 -0
  219. package/providers/team.provider.js +98 -0
  220. package/providers/team.provider.js.map +1 -0
  221. package/utilities/stats/core/helpers.js +105 -0
  222. package/utilities/stats/core/helpers.js.map +1 -0
  223. package/utilities/stats/core/types.js +5 -0
  224. package/utilities/stats/core/types.js.map +1 -0
  225. package/utilities/stats/match/headToHead.js +83 -0
  226. package/utilities/stats/match/headToHead.js.map +1 -0
  227. package/utilities/stats/match/homeVsAway.js +140 -0
  228. package/utilities/stats/match/homeVsAway.js.map +1 -0
  229. package/utilities/stats/match/overUnder.js +91 -0
  230. package/utilities/stats/match/overUnder.js.map +1 -0
  231. package/utilities/stats/match/result.js +21 -0
  232. package/utilities/stats/match/result.js.map +1 -0
  233. package/utilities/stats/team/commonOpponents.js +77 -0
  234. package/utilities/stats/team/commonOpponents.js.map +1 -0
  235. package/utilities/stats/team/goalStats.js +50 -0
  236. package/utilities/stats/team/goalStats.js.map +1 -0
  237. package/utilities/stats/team/streaks.js +183 -0
  238. package/utilities/stats/team/streaks.js.map +1 -0
  239. package/client.cjs +0 -8
  240. package/fansunited-data-layer.cjs +0 -1
  241. package/matches-BvHU-bP0.js +0 -1795
  242. package/matches-Ci0Ci_Mw.cjs +0 -1
@@ -0,0 +1,98 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useState, useEffect, useCallback, useMemo } from "react";
3
+ import { LeaderboardContext } from "./leaderboard.context.js";
4
+ import { useMatchHelpers } from "./leaderboard/hooks/useMatchHelpers.js";
5
+ import { useTemplateHelpers } from "./leaderboard/hooks/useTemplateHelpers.js";
6
+ import { useFansUnitedSDK } from "./fansunited-sdk.hook.js";
7
+ import { getLeaderboardMatches } from "../api/fansunited-sdk/loyalty/matches.js";
8
+ import { getLeaderboardTemplate } from "../api/fansunited-sdk/loyalty/template.js";
9
+ function LeaderboardProvider({
10
+ leaderboardId,
11
+ template: initialTemplate,
12
+ matches: initialMatches,
13
+ config,
14
+ autoRefresh = true,
15
+ refreshInterval = 3e4,
16
+ children
17
+ }) {
18
+ const sdk = useFansUnitedSDK();
19
+ const [template, setTemplate] = useState(initialTemplate);
20
+ const [matches, setMatches] = useState(initialMatches ?? []);
21
+ const [isRefreshing, setIsRefreshing] = useState(false);
22
+ const [lastRefreshed, setLastRefreshed] = useState(void 0);
23
+ useEffect(() => {
24
+ if (template || !sdk) return;
25
+ const fetchInitialData = async () => {
26
+ try {
27
+ const templateData = await getLeaderboardTemplate(sdk, leaderboardId);
28
+ setTemplate(templateData);
29
+ } catch (error) {
30
+ console.error("[LeaderboardProvider] Error fetching initial template:", error);
31
+ }
32
+ };
33
+ fetchInitialData();
34
+ }, [leaderboardId, sdk]);
35
+ const refresh = useCallback(async () => {
36
+ if (!sdk) return;
37
+ setIsRefreshing(true);
38
+ try {
39
+ const matchesData = await getLeaderboardMatches(sdk, leaderboardId);
40
+ setMatches(matchesData);
41
+ setLastRefreshed(/* @__PURE__ */ new Date());
42
+ } catch (error) {
43
+ console.error("[LeaderboardProvider] Error refreshing matches:", error);
44
+ } finally {
45
+ setIsRefreshing(false);
46
+ }
47
+ }, [leaderboardId, sdk]);
48
+ useEffect(() => {
49
+ if (!autoRefresh) return;
50
+ const hasLiveMatches = matches.some((m) => m.status.code === "live");
51
+ if (!hasLiveMatches) return;
52
+ const interval = setInterval(() => {
53
+ refresh();
54
+ }, refreshInterval);
55
+ return () => clearInterval(interval);
56
+ }, [autoRefresh, refreshInterval, matches, refresh]);
57
+ const groups = useMemo(() => template?.groups || [], [template?.groups]);
58
+ const matchHelpers = useMatchHelpers(matches);
59
+ const templateHelpers = useTemplateHelpers(groups, matches);
60
+ const value = useMemo(() => {
61
+ return {
62
+ // Core identifiers
63
+ leaderboardId,
64
+ // Template info
65
+ template,
66
+ // Loading and refresh state
67
+ isRefreshing,
68
+ lastRefreshed,
69
+ // Core data
70
+ matches,
71
+ groups,
72
+ // Custom configuration
73
+ config,
74
+ // Refresh method
75
+ refresh,
76
+ // Template helpers
77
+ ...templateHelpers,
78
+ // Match helpers
79
+ ...matchHelpers
80
+ };
81
+ }, [
82
+ leaderboardId,
83
+ template,
84
+ isRefreshing,
85
+ lastRefreshed,
86
+ matches,
87
+ groups,
88
+ config,
89
+ refresh,
90
+ templateHelpers,
91
+ matchHelpers
92
+ ]);
93
+ return /* @__PURE__ */ jsx(LeaderboardContext.Provider, { value, children });
94
+ }
95
+ export {
96
+ LeaderboardProvider
97
+ };
98
+ //# sourceMappingURL=leaderboard.provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leaderboard.provider.js","sources":["../../src/lib/providers/leaderboard.provider.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Leaderboard/Template Provider\n *\n * Provides predictor/leaderboard template data and matches to all child components.\n * Works for any game type (predictor, fantasy, topx) since templates are the\n * unifying abstraction across all game types.\n *\n * Features:\n * - Automatic template and matches fetching using FansUnited SDK\n * - Auto-refresh for live matches\n * - Helper functions for filtering matches by group, team, status\n * - Template group navigation helpers\n * - Server-side rendering support with initial data\n *\n * @example\n * ```tsx\n * // Client-side usage\n * import { LeaderboardProvider, useLeaderboard } from 'fansunited-data-layer/client';\n *\n * function App() {\n * return (\n * <LeaderboardProvider leaderboardId=\"template-123\">\n * <PredictorGame />\n * </LeaderboardProvider>\n * );\n * }\n *\n * function PredictorGame() {\n * const { template, matches, groups, getGroupMatches } = useLeaderboard();\n * return <div>...</div>;\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Server-side rendering with initial data\n * import { getLeaderboardTemplate, getLeaderboardMatches } from 'fansunited-data-layer';\n * import { LeaderboardProvider } from 'fansunited-data-layer/client';\n *\n * async function ServerComponent() {\n * const sdk = await getSdk(locale);\n * const template = await getLeaderboardTemplate(sdk, 'template-123');\n * const matches = await getLeaderboardMatches(sdk, 'template-123');\n *\n * return (\n * <LeaderboardProvider\n * leaderboardId=\"template-123\"\n * template={template}\n * matches={matches}\n * >\n * <PredictorGame />\n * </LeaderboardProvider>\n * );\n * }\n * ```\n */\n\nimport { useState, useEffect, useMemo, useCallback } from \"react\";\nimport type { FUSportsMatch } from \"../types/canonical\";\nimport { LeaderboardContext } from \"./leaderboard.context\";\nimport type { LeaderboardContextValue, LeaderboardProviderProps, SerializedTemplate } from \"./leaderboard.types\";\nimport { getLeaderboardMatches, getLeaderboardTemplate } from \"../api/fansunited-sdk/loyalty\";\nimport { useMatchHelpers } from \"./leaderboard/hooks/useMatchHelpers\";\nimport { useTemplateHelpers } from \"./leaderboard/hooks/useTemplateHelpers\";\nimport { useFansUnitedSDK } from \"./fansunited-sdk.hook\";\n\n// ============================================================================\n// Provider Component\n// ============================================================================\n\nexport function LeaderboardProvider({\n leaderboardId,\n template: initialTemplate,\n matches: initialMatches,\n config,\n autoRefresh = true,\n refreshInterval = 30000,\n children,\n}: LeaderboardProviderProps) {\n // Get SDK instance\n const sdk = useFansUnitedSDK();\n\n // Core data state\n const [template, setTemplate] = useState<SerializedTemplate | undefined>(initialTemplate);\n const [matches, setMatches] = useState<FUSportsMatch[]>(initialMatches ?? []);\n\n // Refresh state\n const [isRefreshing, setIsRefreshing] = useState(false);\n const [lastRefreshed, setLastRefreshed] = useState<Date | undefined>(undefined);\n\n // Initial fetch for template (only once if not provided)\n useEffect(() => {\n if (template || !sdk) return;\n\n const fetchInitialData = async () => {\n try {\n const templateData = await getLeaderboardTemplate(sdk, leaderboardId);\n setTemplate(templateData);\n } catch (error) {\n console.error(\"[LeaderboardProvider] Error fetching initial template:\", error);\n }\n };\n\n fetchInitialData();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [leaderboardId, sdk]);\n\n // Refresh function for matches\n const refresh = useCallback(async () => {\n if (!sdk) return;\n\n setIsRefreshing(true);\n try {\n const matchesData = await getLeaderboardMatches(sdk, leaderboardId);\n setMatches(matchesData);\n setLastRefreshed(new Date());\n } catch (error) {\n console.error(\"[LeaderboardProvider] Error refreshing matches:\", error);\n } finally {\n setIsRefreshing(false);\n }\n }, [leaderboardId, sdk]);\n\n // Auto-refresh for live matches\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 groups = useMemo(() => template?.groups || [], [template?.groups]);\n const matchHelpers = useMatchHelpers(matches);\n const templateHelpers = useTemplateHelpers(groups, matches);\n\n // Memoize context value\n const value = useMemo<LeaderboardContextValue>(() => {\n return {\n // Core identifiers\n leaderboardId,\n\n // Template info\n template,\n\n // Loading and refresh state\n isRefreshing,\n lastRefreshed,\n\n // Core data\n matches,\n groups,\n\n // Custom configuration\n config,\n\n // Refresh method\n refresh,\n\n // Template helpers\n ...templateHelpers,\n\n // Match helpers\n ...matchHelpers,\n };\n }, [\n leaderboardId,\n template,\n isRefreshing,\n lastRefreshed,\n matches,\n groups,\n config,\n refresh,\n templateHelpers,\n matchHelpers,\n ]);\n\n return <LeaderboardContext.Provider value={value}>{children}</LeaderboardContext.Provider>;\n}\n"],"names":[],"mappings":";;;;;;;;AAwEO,SAAS,oBAAoB;AAAA,EAChC;AAAA,EACA,UAAU;AAAA,EACV,SAAS;AAAA,EACT;AAAA,EACA,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB;AACJ,GAA6B;AAEzB,QAAM,MAAM,iBAAA;AAGZ,QAAM,CAAC,UAAU,WAAW,IAAI,SAAyC,eAAe;AACxF,QAAM,CAAC,SAAS,UAAU,IAAI,SAA0B,kBAAkB,CAAA,CAAE;AAG5E,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AACtD,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAA2B,MAAS;AAG9E,YAAU,MAAM;AACZ,QAAI,YAAY,CAAC,IAAK;AAEtB,UAAM,mBAAmB,YAAY;AACjC,UAAI;AACA,cAAM,eAAe,MAAM,uBAAuB,KAAK,aAAa;AACpE,oBAAY,YAAY;AAAA,MAC5B,SAAS,OAAO;AACZ,gBAAQ,MAAM,0DAA0D,KAAK;AAAA,MACjF;AAAA,IACJ;AAEA,qBAAA;AAAA,EAEJ,GAAG,CAAC,eAAe,GAAG,CAAC;AAGvB,QAAM,UAAU,YAAY,YAAY;AACpC,QAAI,CAAC,IAAK;AAEV,oBAAgB,IAAI;AACpB,QAAI;AACA,YAAM,cAAc,MAAM,sBAAsB,KAAK,aAAa;AAClE,iBAAW,WAAW;AACtB,uBAAiB,oBAAI,MAAM;AAAA,IAC/B,SAAS,OAAO;AACZ,cAAQ,MAAM,mDAAmD,KAAK;AAAA,IAC1E,UAAA;AACI,sBAAgB,KAAK;AAAA,IACzB;AAAA,EACJ,GAAG,CAAC,eAAe,GAAG,CAAC;AAGvB,YAAU,MAAM;AACZ,QAAI,CAAC,YAAa;AAElB,UAAM,iBAAiB,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS,MAAM;AACnE,QAAI,CAAC,eAAgB;AAErB,UAAM,WAAW,YAAY,MAAM;AAC/B,cAAA;AAAA,IACJ,GAAG,eAAe;AAElB,WAAO,MAAM,cAAc,QAAQ;AAAA,EACvC,GAAG,CAAC,aAAa,iBAAiB,SAAS,OAAO,CAAC;AAGnD,QAAM,SAAS,QAAQ,MAAM,UAAU,UAAU,IAAI,CAAC,UAAU,MAAM,CAAC;AACvE,QAAM,eAAe,gBAAgB,OAAO;AAC5C,QAAM,kBAAkB,mBAAmB,QAAQ,OAAO;AAG1D,QAAM,QAAQ,QAAiC,MAAM;AACjD,WAAO;AAAA;AAAA,MAEH;AAAA;AAAA,MAGA;AAAA;AAAA,MAGA;AAAA,MACA;AAAA;AAAA,MAGA;AAAA,MACA;AAAA;AAAA,MAGA;AAAA;AAAA,MAGA;AAAA;AAAA,MAGA,GAAG;AAAA;AAAA,MAGH,GAAG;AAAA,IAAA;AAAA,EAEX,GAAG;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACH;AAED,SAAO,oBAAC,mBAAmB,UAAnB,EAA4B,OAAe,SAAA,CAAS;AAChE;"}
@@ -0,0 +1,72 @@
1
+ import { useState, useRef, useMemo, useCallback, useEffect } from "react";
2
+ import { useFansUnitedSDK } from "../../fansunited-sdk.hook.js";
3
+ import { getBatchMatchOdds } from "../../../api/fansunited-sdk/odds/matches.js";
4
+ const DEFAULT_ODDS_POLLING_INTERVAL_MS = 3e4;
5
+ function useBatchMatchOdds({
6
+ matchIds,
7
+ markets = ["1x2"],
8
+ operatorId,
9
+ oddType = "ALL",
10
+ isLive = false,
11
+ pollingIntervalMs = DEFAULT_ODDS_POLLING_INTERVAL_MS
12
+ }) {
13
+ const sdk = useFansUnitedSDK();
14
+ const [oddsMap, setOddsMap] = useState(/* @__PURE__ */ new Map());
15
+ const [loading, setLoading] = useState(true);
16
+ const [error, setError] = useState(null);
17
+ const isInitialLoad = useRef(true);
18
+ const matchIdsKey = useMemo(() => matchIds.join(","), [matchIds]);
19
+ const marketsKey = useMemo(() => markets.join(","), [markets]);
20
+ const fetchBatchOdds = useCallback(
21
+ async (isRefresh = false) => {
22
+ if (!sdk || matchIds.length === 0) {
23
+ setLoading(false);
24
+ return;
25
+ }
26
+ try {
27
+ if (!isRefresh) {
28
+ setLoading(true);
29
+ }
30
+ setError(null);
31
+ const response = await getBatchMatchOdds(sdk, matchIds, {
32
+ marketTypes: markets,
33
+ bookmakerIds: operatorId ? [operatorId] : void 0,
34
+ oddType
35
+ });
36
+ setOddsMap(response);
37
+ } catch (err) {
38
+ console.error("Error fetching batch odds:", err);
39
+ setError(err instanceof Error ? err : new Error("Failed to fetch odds"));
40
+ } finally {
41
+ setLoading(false);
42
+ isInitialLoad.current = false;
43
+ }
44
+ },
45
+ // eslint-disable-next-line react-hooks/exhaustive-deps
46
+ [sdk, matchIdsKey, marketsKey, operatorId, oddType]
47
+ );
48
+ useEffect(() => {
49
+ isInitialLoad.current = true;
50
+ fetchBatchOdds(false);
51
+ }, [fetchBatchOdds]);
52
+ useEffect(() => {
53
+ if (!isLive || matchIds.length === 0) {
54
+ return;
55
+ }
56
+ const intervalId = setInterval(() => {
57
+ fetchBatchOdds(true);
58
+ }, pollingIntervalMs);
59
+ return () => {
60
+ clearInterval(intervalId);
61
+ };
62
+ }, [isLive, matchIds.length, pollingIntervalMs, fetchBatchOdds]);
63
+ const refetch = useCallback(() => {
64
+ fetchBatchOdds(true);
65
+ }, [fetchBatchOdds]);
66
+ return { oddsMap, loading, error, refetch };
67
+ }
68
+ export {
69
+ DEFAULT_ODDS_POLLING_INTERVAL_MS,
70
+ useBatchMatchOdds
71
+ };
72
+ //# sourceMappingURL=useBatchMatchOdds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBatchMatchOdds.js","sources":["../../../../src/lib/providers/match/hooks/useBatchMatchOdds.ts"],"sourcesContent":["import { useEffect, useState, useMemo, useCallback, useRef } from \"react\";\nimport { useFansUnitedSDK } from \"../../fansunited-sdk.hook\";\nimport type { FUSportsBatchMatchOdds } from \"../../../types/canonical\";\nimport type { MarketType } from \"../../../api/sportal365-sports/football/matches/types\";\nimport { getBatchMatchOdds } from \"../../../api/fansunited-sdk/odds\";\n\n/** Default polling interval in milliseconds (30 seconds) */\nexport const DEFAULT_ODDS_POLLING_INTERVAL_MS = 30000;\n\n/**\n * Options for useBatchMatchOdds hook\n */\nexport interface UseBatchMatchOddsOptions {\n /** Array of match IDs to fetch odds for */\n matchIds: string[];\n /** Market types to include (default: [\"1x2\"]) */\n markets?: MarketType[];\n /** Filter odds by specific bookmaker/operator ID */\n operatorId?: string;\n /** Type of odds: PRE_EVENT, LIVE, or ALL (default: ALL) */\n oddType?: \"PRE_EVENT\" | \"LIVE\" | \"ALL\";\n /** Whether the matches are live - enables polling when true */\n isLive?: boolean;\n /** Polling interval in milliseconds (default: 30000ms / 30 seconds) */\n pollingIntervalMs?: number;\n}\n\n/**\n * Custom hook to fetch betting odds for multiple matches in a single API call\n *\n * This hook provides efficient batch fetching of odds with automatic polling for live matches.\n * It uses the SDK's batch odds endpoint to minimize API calls.\n *\n * @param options - Configuration options for fetching odds\n * @returns Object containing odds map, loading state, error, and refetch function\n *\n * @example\n * ```typescript\n * import { useBatchMatchOdds } from 'fansunited-data-layer/client';\n *\n * function MatchOddsWidget({ matchIds, isLive }) {\n * const { oddsMap, loading, error, refetch } = useBatchMatchOdds({\n * matchIds,\n * markets: ['1x2', 'OVER_UNDER'],\n * isLive,\n * pollingIntervalMs: 30000\n * });\n *\n * if (loading) return <div>Loading odds...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return (\n * <div>\n * {Array.from(oddsMap.entries()).map(([matchId, operators]) => (\n * <div key={matchId}>\n * <h3>Match {matchId}</h3>\n * {operators.map(op => (\n * <div key={op.operator.id}>\n * <span>{op.operator.name}</span>\n * {op.markets[0]?.selections.map(sel => (\n * <span key={sel.id}>\n * {sel.name}: {sel.odds}\n * {sel.movement !== 'NONE' && <span>↑</span>}\n * </span>\n * ))}\n * </div>\n * ))}\n * </div>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useBatchMatchOdds({\n matchIds,\n markets = [\"1x2\"],\n operatorId,\n oddType = \"ALL\",\n isLive = false,\n pollingIntervalMs = DEFAULT_ODDS_POLLING_INTERVAL_MS,\n}: UseBatchMatchOddsOptions) {\n const sdk = useFansUnitedSDK();\n const [oddsMap, setOddsMap] = useState<FUSportsBatchMatchOdds>(new Map());\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Track if this is the initial load vs a refresh\n const isInitialLoad = useRef(true);\n\n // Create stable keys for arrays to avoid unnecessary re-fetches\n const matchIdsKey = useMemo(() => matchIds.join(\",\"), [matchIds]);\n const marketsKey = useMemo(() => markets.join(\",\"), [markets]);\n\n const fetchBatchOdds = useCallback(\n async (isRefresh = false) => {\n if (!sdk || matchIds.length === 0) {\n setLoading(false);\n return;\n }\n\n try {\n // Only show loading on initial load, not on refreshes\n if (!isRefresh) {\n setLoading(true);\n }\n setError(null);\n\n // Call the batch odds API\n const response = await getBatchMatchOdds(sdk, matchIds, {\n marketTypes: markets,\n bookmakerIds: operatorId ? [operatorId] : undefined,\n oddType,\n });\n\n setOddsMap(response);\n } catch (err) {\n console.error(\"Error fetching batch odds:\", err);\n setError(err instanceof Error ? err : new Error(\"Failed to fetch odds\"));\n } finally {\n setLoading(false);\n isInitialLoad.current = false;\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [sdk, matchIdsKey, marketsKey, operatorId, oddType]\n );\n\n // Initial load\n useEffect(() => {\n isInitialLoad.current = true;\n fetchBatchOdds(false);\n }, [fetchBatchOdds]);\n\n // Set up polling for live matches\n useEffect(() => {\n if (!isLive || matchIds.length === 0) {\n return;\n }\n\n const intervalId = setInterval(() => {\n fetchBatchOdds(true);\n }, pollingIntervalMs);\n\n return () => {\n clearInterval(intervalId);\n };\n }, [isLive, matchIds.length, pollingIntervalMs, fetchBatchOdds]);\n\n // Manual refetch function\n const refetch = useCallback(() => {\n fetchBatchOdds(true);\n }, [fetchBatchOdds]);\n\n return { oddsMap, loading, error, refetch };\n}\n\n"],"names":[],"mappings":";;;AAOO,MAAM,mCAAmC;AAmEzC,SAAS,kBAAkB;AAAA,EAC9B;AAAA,EACA,UAAU,CAAC,KAAK;AAAA,EAChB;AAAA,EACA,UAAU;AAAA,EACV,SAAS;AAAA,EACT,oBAAoB;AACxB,GAA6B;AACzB,QAAM,MAAM,iBAAA;AACZ,QAAM,CAAC,SAAS,UAAU,IAAI,SAAiC,oBAAI,KAAK;AACxE,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,IAAI;AAC3C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB,IAAI;AAGrD,QAAM,gBAAgB,OAAO,IAAI;AAGjC,QAAM,cAAc,QAAQ,MAAM,SAAS,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC;AAChE,QAAM,aAAa,QAAQ,MAAM,QAAQ,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC;AAE7D,QAAM,iBAAiB;AAAA,IACnB,OAAO,YAAY,UAAU;AACzB,UAAI,CAAC,OAAO,SAAS,WAAW,GAAG;AAC/B,mBAAW,KAAK;AAChB;AAAA,MACJ;AAEA,UAAI;AAEA,YAAI,CAAC,WAAW;AACZ,qBAAW,IAAI;AAAA,QACnB;AACA,iBAAS,IAAI;AAGb,cAAM,WAAW,MAAM,kBAAkB,KAAK,UAAU;AAAA,UACpD,aAAa;AAAA,UACb,cAAc,aAAa,CAAC,UAAU,IAAI;AAAA,UAC1C;AAAA,QAAA,CACH;AAED,mBAAW,QAAQ;AAAA,MACvB,SAAS,KAAK;AACV,gBAAQ,MAAM,8BAA8B,GAAG;AAC/C,iBAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,sBAAsB,CAAC;AAAA,MAC3E,UAAA;AACI,mBAAW,KAAK;AAChB,sBAAc,UAAU;AAAA,MAC5B;AAAA,IACJ;AAAA;AAAA,IAEA,CAAC,KAAK,aAAa,YAAY,YAAY,OAAO;AAAA,EAAA;AAItD,YAAU,MAAM;AACZ,kBAAc,UAAU;AACxB,mBAAe,KAAK;AAAA,EACxB,GAAG,CAAC,cAAc,CAAC;AAGnB,YAAU,MAAM;AACZ,QAAI,CAAC,UAAU,SAAS,WAAW,GAAG;AAClC;AAAA,IACJ;AAEA,UAAM,aAAa,YAAY,MAAM;AACjC,qBAAe,IAAI;AAAA,IACvB,GAAG,iBAAiB;AAEpB,WAAO,MAAM;AACT,oBAAc,UAAU;AAAA,IAC5B;AAAA,EACJ,GAAG,CAAC,QAAQ,SAAS,QAAQ,mBAAmB,cAAc,CAAC;AAG/D,QAAM,UAAU,YAAY,MAAM;AAC9B,mBAAe,IAAI;AAAA,EACvB,GAAG,CAAC,cAAc,CAAC;AAEnB,SAAO,EAAE,SAAS,SAAS,OAAO,QAAA;AACtC;"}
@@ -0,0 +1,46 @@
1
+ import { useMemo, useCallback } from "react";
2
+ function useEventHelpers(match, events) {
3
+ const allEvents = useMemo(() => events || match?.mainEvents || [], [events, match]);
4
+ const getGoals = useCallback(() => {
5
+ return allEvents.filter(
6
+ (e) => e.type === "GOAL" || e.type === "PENALTY_GOAL" || e.type === "OWN_GOAL" || e.type === "PENALTY_SHOOTOUT_SCORED"
7
+ );
8
+ }, [allEvents]);
9
+ const getCards = useCallback(() => {
10
+ return allEvents.filter(
11
+ (e) => e.type === "YELLOW_CARD" || e.type === "RED_CARD" || e.type === "YELLOW_RED_CARD"
12
+ );
13
+ }, [allEvents]);
14
+ const getSubstitutions = useCallback(() => {
15
+ return allEvents.filter((e) => e.type === "SUBSTITUTION");
16
+ }, [allEvents]);
17
+ const getEventsByTeam = useCallback(
18
+ (teamId) => {
19
+ if (!match) return [];
20
+ const isHome = match.competitorOne.id === teamId;
21
+ const position = isHome ? "ONE" : "TWO";
22
+ return allEvents.filter((e) => e.competitorPosition === position);
23
+ },
24
+ [match, allEvents]
25
+ );
26
+ const getEventsByPeriod = useCallback(
27
+ (period) => {
28
+ return allEvents.filter((e) => e.period === period);
29
+ },
30
+ [allEvents]
31
+ );
32
+ return useMemo(
33
+ () => ({
34
+ getGoals,
35
+ getCards,
36
+ getSubstitutions,
37
+ getEventsByTeam,
38
+ getEventsByPeriod
39
+ }),
40
+ [getGoals, getCards, getSubstitutions, getEventsByTeam, getEventsByPeriod]
41
+ );
42
+ }
43
+ export {
44
+ useEventHelpers
45
+ };
46
+ //# sourceMappingURL=useEventHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEventHelpers.js","sources":["../../../../src/lib/providers/match/hooks/useEventHelpers.ts"],"sourcesContent":["import { useCallback, useMemo } from \"react\";\nimport type { FUSportsMatch, FUSportsMatchEvent } from \"../../../types/canonical\";\n\n/**\n * Hook for event-related helper functions\n */\nexport function useEventHelpers(match: FUSportsMatch | undefined, events?: FUSportsMatchEvent[]) {\n // Use events from match.mainEvents if not provided separately\n const allEvents = useMemo(() => events || match?.mainEvents || [], [events, match]);\n\n const getGoals = useCallback(() => {\n return allEvents.filter(\n (e) =>\n e.type === \"GOAL\" ||\n e.type === \"PENALTY_GOAL\" ||\n e.type === \"OWN_GOAL\" ||\n e.type === \"PENALTY_SHOOTOUT_SCORED\"\n );\n }, [allEvents]);\n\n const getCards = useCallback(() => {\n return allEvents.filter(\n (e) => e.type === \"YELLOW_CARD\" || e.type === \"RED_CARD\" || e.type === \"YELLOW_RED_CARD\"\n );\n }, [allEvents]);\n\n const getSubstitutions = useCallback(() => {\n return allEvents.filter((e) => e.type === \"SUBSTITUTION\");\n }, [allEvents]);\n\n const getEventsByTeam = useCallback(\n (teamId: string) => {\n if (!match) return [];\n\n const isHome = match.competitorOne.id === teamId;\n const position = isHome ? \"ONE\" : \"TWO\";\n\n return allEvents.filter((e) => e.competitorPosition === position);\n },\n [match, allEvents]\n );\n\n const getEventsByPeriod = useCallback(\n (period: string) => {\n return allEvents.filter((e) => e.period === period);\n },\n [allEvents]\n );\n\n return useMemo(\n () => ({\n getGoals,\n getCards,\n getSubstitutions,\n getEventsByTeam,\n getEventsByPeriod,\n }),\n [getGoals, getCards, getSubstitutions, getEventsByTeam, getEventsByPeriod]\n );\n}\n"],"names":[],"mappings":";AAMO,SAAS,gBAAgB,OAAkC,QAA+B;AAE7F,QAAM,YAAY,QAAQ,MAAM,UAAU,OAAO,cAAc,IAAI,CAAC,QAAQ,KAAK,CAAC;AAElF,QAAM,WAAW,YAAY,MAAM;AAC/B,WAAO,UAAU;AAAA,MACb,CAAC,MACG,EAAE,SAAS,UACX,EAAE,SAAS,kBACX,EAAE,SAAS,cACX,EAAE,SAAS;AAAA,IAAA;AAAA,EAEvB,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,WAAW,YAAY,MAAM;AAC/B,WAAO,UAAU;AAAA,MACb,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,SAAS,cAAc,EAAE,SAAS;AAAA,IAAA;AAAA,EAE/E,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,mBAAmB,YAAY,MAAM;AACvC,WAAO,UAAU,OAAO,CAAC,MAAM,EAAE,SAAS,cAAc;AAAA,EAC5D,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,kBAAkB;AAAA,IACpB,CAAC,WAAmB;AAChB,UAAI,CAAC,MAAO,QAAO,CAAA;AAEnB,YAAM,SAAS,MAAM,cAAc,OAAO;AAC1C,YAAM,WAAW,SAAS,QAAQ;AAElC,aAAO,UAAU,OAAO,CAAC,MAAM,EAAE,uBAAuB,QAAQ;AAAA,IACpE;AAAA,IACA,CAAC,OAAO,SAAS;AAAA,EAAA;AAGrB,QAAM,oBAAoB;AAAA,IACtB,CAAC,WAAmB;AAChB,aAAO,UAAU,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM;AAAA,IACtD;AAAA,IACA,CAAC,SAAS;AAAA,EAAA;AAGd,SAAO;AAAA,IACH,OAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAEJ,CAAC,UAAU,UAAU,kBAAkB,iBAAiB,iBAAiB;AAAA,EAAA;AAEjF;"}
@@ -0,0 +1,52 @@
1
+ import { useCallback, useMemo } from "react";
2
+ function useHeadToHeadHelpers(match, headToHeadMatches) {
3
+ const getHeadToHeadStats = useCallback(() => {
4
+ if (!headToHeadMatches || !match) {
5
+ return { homeWins: 0, awayWins: 0, draws: 0 };
6
+ }
7
+ let homeWins = 0;
8
+ let awayWins = 0;
9
+ let draws = 0;
10
+ const homeTeamId = match.competitorOne.id;
11
+ for (const h2hMatch of headToHeadMatches) {
12
+ if (h2hMatch.status.type !== "FINISHED" || !h2hMatch.score) continue;
13
+ const homeScore = parseInt(h2hMatch.score.competitorOne || "0", 10);
14
+ const awayScore = parseInt(h2hMatch.score.competitorTwo || "0", 10);
15
+ const wasHomeTeamHome = h2hMatch.competitorOne.id === homeTeamId;
16
+ if (homeScore > awayScore) {
17
+ if (wasHomeTeamHome) {
18
+ homeWins++;
19
+ } else {
20
+ awayWins++;
21
+ }
22
+ } else if (awayScore > homeScore) {
23
+ if (wasHomeTeamHome) {
24
+ awayWins++;
25
+ } else {
26
+ homeWins++;
27
+ }
28
+ } else {
29
+ draws++;
30
+ }
31
+ }
32
+ return { homeWins, awayWins, draws };
33
+ }, [headToHeadMatches, match]);
34
+ const getLastMeetings = useCallback(
35
+ (limit = 5) => {
36
+ if (!headToHeadMatches) return [];
37
+ return headToHeadMatches.filter((m) => m.status.type === "FINISHED").sort((a, b) => b.timing.kickoffTime.getTime() - a.timing.kickoffTime.getTime()).slice(0, limit);
38
+ },
39
+ [headToHeadMatches]
40
+ );
41
+ return useMemo(
42
+ () => ({
43
+ getHeadToHeadStats,
44
+ getLastMeetings
45
+ }),
46
+ [getHeadToHeadStats, getLastMeetings]
47
+ );
48
+ }
49
+ export {
50
+ useHeadToHeadHelpers
51
+ };
52
+ //# sourceMappingURL=useHeadToHeadHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useHeadToHeadHelpers.js","sources":["../../../../src/lib/providers/match/hooks/useHeadToHeadHelpers.ts"],"sourcesContent":["import { useCallback, useMemo } from \"react\";\nimport type { FUSportsMatch } from \"../../../types/canonical\";\n\n/**\n * Hook for head-to-head helper functions\n */\nexport function useHeadToHeadHelpers(match: FUSportsMatch | undefined, headToHeadMatches?: FUSportsMatch[]) {\n const getHeadToHeadStats = useCallback((): { homeWins: number; awayWins: number; draws: number } => {\n if (!headToHeadMatches || !match) {\n return { homeWins: 0, awayWins: 0, draws: 0 };\n }\n\n let homeWins = 0;\n let awayWins = 0;\n let draws = 0;\n\n const homeTeamId = match.competitorOne.id;\n\n for (const h2hMatch of headToHeadMatches) {\n if (h2hMatch.status.type !== \"FINISHED\" || !h2hMatch.score) continue;\n\n const homeScore = parseInt(h2hMatch.score.competitorOne || \"0\", 10);\n const awayScore = parseInt(h2hMatch.score.competitorTwo || \"0\", 10);\n\n // Determine which team was home in the H2H match\n const wasHomeTeamHome = h2hMatch.competitorOne.id === homeTeamId;\n\n if (homeScore > awayScore) {\n // Home team won the H2H match\n if (wasHomeTeamHome) {\n homeWins++;\n } else {\n awayWins++;\n }\n } else if (awayScore > homeScore) {\n // Away team won the H2H match\n if (wasHomeTeamHome) {\n awayWins++;\n } else {\n homeWins++;\n }\n } else {\n draws++;\n }\n }\n\n return { homeWins, awayWins, draws };\n }, [headToHeadMatches, match]);\n\n const getLastMeetings = useCallback(\n (limit: number = 5): FUSportsMatch[] => {\n if (!headToHeadMatches) return [];\n\n return headToHeadMatches\n .filter((m) => m.status.type === \"FINISHED\")\n .sort((a, b) => b.timing.kickoffTime.getTime() - a.timing.kickoffTime.getTime())\n .slice(0, limit);\n },\n [headToHeadMatches]\n );\n\n return useMemo(\n () => ({\n getHeadToHeadStats,\n getLastMeetings,\n }),\n [getHeadToHeadStats, getLastMeetings]\n );\n}\n"],"names":[],"mappings":";AAMO,SAAS,qBAAqB,OAAkC,mBAAqC;AACxG,QAAM,qBAAqB,YAAY,MAA6D;AAChG,QAAI,CAAC,qBAAqB,CAAC,OAAO;AAC9B,aAAO,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,EAAA;AAAA,IAC9C;AAEA,QAAI,WAAW;AACf,QAAI,WAAW;AACf,QAAI,QAAQ;AAEZ,UAAM,aAAa,MAAM,cAAc;AAEvC,eAAW,YAAY,mBAAmB;AACtC,UAAI,SAAS,OAAO,SAAS,cAAc,CAAC,SAAS,MAAO;AAE5D,YAAM,YAAY,SAAS,SAAS,MAAM,iBAAiB,KAAK,EAAE;AAClE,YAAM,YAAY,SAAS,SAAS,MAAM,iBAAiB,KAAK,EAAE;AAGlE,YAAM,kBAAkB,SAAS,cAAc,OAAO;AAEtD,UAAI,YAAY,WAAW;AAEvB,YAAI,iBAAiB;AACjB;AAAA,QACJ,OAAO;AACH;AAAA,QACJ;AAAA,MACJ,WAAW,YAAY,WAAW;AAE9B,YAAI,iBAAiB;AACjB;AAAA,QACJ,OAAO;AACH;AAAA,QACJ;AAAA,MACJ,OAAO;AACH;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO,EAAE,UAAU,UAAU,MAAA;AAAA,EACjC,GAAG,CAAC,mBAAmB,KAAK,CAAC;AAE7B,QAAM,kBAAkB;AAAA,IACpB,CAAC,QAAgB,MAAuB;AACpC,UAAI,CAAC,kBAAmB,QAAO,CAAA;AAE/B,aAAO,kBACF,OAAO,CAAC,MAAM,EAAE,OAAO,SAAS,UAAU,EAC1C,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,YAAY,QAAA,IAAY,EAAE,OAAO,YAAY,SAAS,EAC9E,MAAM,GAAG,KAAK;AAAA,IACvB;AAAA,IACA,CAAC,iBAAiB;AAAA,EAAA;AAGtB,SAAO;AAAA,IACH,OAAO;AAAA,MACH;AAAA,MACA;AAAA,IAAA;AAAA,IAEJ,CAAC,oBAAoB,eAAe;AAAA,EAAA;AAE5C;"}
@@ -0,0 +1,53 @@
1
+ import { useCallback, useMemo } from "react";
2
+ function useLineupHelpers(match, lineups) {
3
+ const getHomeLineup = useCallback(() => {
4
+ return lineups?.competitorOne;
5
+ }, [lineups]);
6
+ const getAwayLineup = useCallback(() => {
7
+ return lineups?.competitorTwo;
8
+ }, [lineups]);
9
+ const getTeamLineup = useCallback(
10
+ (teamId) => {
11
+ if (!lineups || !match) return void 0;
12
+ const isHome = match.competitorOne.id === teamId;
13
+ return isHome ? lineups.competitorOne : lineups.competitorTwo;
14
+ },
15
+ [lineups, match]
16
+ );
17
+ const getStartingEleven = useCallback(
18
+ (teamId) => {
19
+ const lineup = getTeamLineup(teamId);
20
+ return lineup?.starters || [];
21
+ },
22
+ [getTeamLineup]
23
+ );
24
+ const getSubstitutes = useCallback(
25
+ (teamId) => {
26
+ const lineup = getTeamLineup(teamId);
27
+ return lineup?.substitutes || [];
28
+ },
29
+ [getTeamLineup]
30
+ );
31
+ const getFormation = useCallback(
32
+ (teamId) => {
33
+ const lineup = getTeamLineup(teamId);
34
+ return lineup?.formation;
35
+ },
36
+ [getTeamLineup]
37
+ );
38
+ return useMemo(
39
+ () => ({
40
+ getHomeLineup,
41
+ getAwayLineup,
42
+ getTeamLineup,
43
+ getStartingEleven,
44
+ getSubstitutes,
45
+ getFormation
46
+ }),
47
+ [getHomeLineup, getAwayLineup, getTeamLineup, getStartingEleven, getSubstitutes, getFormation]
48
+ );
49
+ }
50
+ export {
51
+ useLineupHelpers
52
+ };
53
+ //# sourceMappingURL=useLineupHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLineupHelpers.js","sources":["../../../../src/lib/providers/match/hooks/useLineupHelpers.ts"],"sourcesContent":["import { useCallback, useMemo } from \"react\";\nimport type {\n FUSportsMatch,\n FUSportsMatchLineups,\n FUSportsCompetitorLineup,\n FUSportsLineupMember,\n} from \"../../../types/canonical\";\n\n/**\n * Hook for lineup-related helper functions\n */\nexport function useLineupHelpers(match: FUSportsMatch | undefined, lineups?: FUSportsMatchLineups) {\n const getHomeLineup = useCallback((): FUSportsCompetitorLineup | undefined => {\n return lineups?.competitorOne;\n }, [lineups]);\n\n const getAwayLineup = useCallback((): FUSportsCompetitorLineup | undefined => {\n return lineups?.competitorTwo;\n }, [lineups]);\n\n const getTeamLineup = useCallback(\n (teamId: string): FUSportsCompetitorLineup | undefined => {\n if (!lineups || !match) return undefined;\n\n const isHome = match.competitorOne.id === teamId;\n return isHome ? lineups.competitorOne : lineups.competitorTwo;\n },\n [lineups, match]\n );\n\n const getStartingEleven = useCallback(\n (teamId: string): FUSportsLineupMember[] => {\n const lineup = getTeamLineup(teamId);\n return lineup?.starters || [];\n },\n [getTeamLineup]\n );\n\n const getSubstitutes = useCallback(\n (teamId: string): FUSportsLineupMember[] => {\n const lineup = getTeamLineup(teamId);\n return lineup?.substitutes || [];\n },\n [getTeamLineup]\n );\n\n const getFormation = useCallback(\n (teamId: string): string | undefined => {\n const lineup = getTeamLineup(teamId);\n return lineup?.formation;\n },\n [getTeamLineup]\n );\n\n return useMemo(\n () => ({\n getHomeLineup,\n getAwayLineup,\n getTeamLineup,\n getStartingEleven,\n getSubstitutes,\n getFormation,\n }),\n [getHomeLineup, getAwayLineup, getTeamLineup, getStartingEleven, getSubstitutes, getFormation]\n );\n}\n"],"names":[],"mappings":";AAWO,SAAS,iBAAiB,OAAkC,SAAgC;AAC/F,QAAM,gBAAgB,YAAY,MAA4C;AAC1E,WAAO,SAAS;AAAA,EACpB,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,gBAAgB,YAAY,MAA4C;AAC1E,WAAO,SAAS;AAAA,EACpB,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,gBAAgB;AAAA,IAClB,CAAC,WAAyD;AACtD,UAAI,CAAC,WAAW,CAAC,MAAO,QAAO;AAE/B,YAAM,SAAS,MAAM,cAAc,OAAO;AAC1C,aAAO,SAAS,QAAQ,gBAAgB,QAAQ;AAAA,IACpD;AAAA,IACA,CAAC,SAAS,KAAK;AAAA,EAAA;AAGnB,QAAM,oBAAoB;AAAA,IACtB,CAAC,WAA2C;AACxC,YAAM,SAAS,cAAc,MAAM;AACnC,aAAO,QAAQ,YAAY,CAAA;AAAA,IAC/B;AAAA,IACA,CAAC,aAAa;AAAA,EAAA;AAGlB,QAAM,iBAAiB;AAAA,IACnB,CAAC,WAA2C;AACxC,YAAM,SAAS,cAAc,MAAM;AACnC,aAAO,QAAQ,eAAe,CAAA;AAAA,IAClC;AAAA,IACA,CAAC,aAAa;AAAA,EAAA;AAGlB,QAAM,eAAe;AAAA,IACjB,CAAC,WAAuC;AACpC,YAAM,SAAS,cAAc,MAAM;AACnC,aAAO,QAAQ;AAAA,IACnB;AAAA,IACA,CAAC,aAAa;AAAA,EAAA;AAGlB,SAAO;AAAA,IACH,OAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAEJ,CAAC,eAAe,eAAe,eAAe,mBAAmB,gBAAgB,YAAY;AAAA,EAAA;AAErG;"}