Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,411 changes: 1,244 additions & 167 deletions package-lock.json

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,18 @@
"dependencies": {
"@supabase/ssr": "^0.10.2",
"@supabase/supabase-js": "^2.103.0",
"next": "16.2.3",
"@types/d3": "^7.4.3",
"@upstash/redis": "^1.38.0",
"ai": "^6.0.191",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"d3": "^7.9.0",
"lucide-react": "^1.16.0",
"next": "^16.2.6",
"openai": "^6.39.0",
"react": "19.2.4",
"react-dom": "19.2.4"
"react-dom": "19.2.4",
"recharts": "^3.8.1"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
Expand Down
10 changes: 10 additions & 0 deletions src/app/dashboard/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Dashboard Layout - Placeholder for Phase 2
*/
export default function DashboardLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return <>{children}</>;
}
6 changes: 6 additions & 0 deletions src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* Placeholder - will be replaced in Phase 2
*/
export default function DashboardPage() {
return <div>Placeholder</div>;
}
132 changes: 121 additions & 11 deletions src/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,26 +1,136 @@
@import "tailwindcss";

:root {
--background: #ffffff;
--foreground: #171717;
}
/* Primary Colors */
--primary: #00ff87;
--primary-dark: #00d966;
--primary-light: #33ff99;

/* Background Shades */
--background: #0d0d0d;
--background-secondary: #1a1a1a;
--background-tertiary: #262626;

/* Foreground / Text */
--foreground: #f5f5f5;
--foreground-secondary: #b0b0b0;
--foreground-tertiary: #808080;

/* Accent Colors */
--accent: #00ff87;
--accent-red: #ff4444;
--accent-yellow: #ffd700;
--accent-green: #00ff87;

/* Borders & Dividers */
--border: #333333;
--border-light: #404040;

@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
/* Semantic Colors */
--success: #00ff87;
--warning: #ffd700;
--error: #ff4444;
--info: #3d00ff;

/* Fonts */
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}

@media (prefers-color-scheme: dark) {
@media (prefers-color-scheme: light) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
--background: #ffffff;
--background-secondary: #f9f9f9;
--background-tertiary: #f0f0f0;
--foreground: #0d0d0d;
--foreground-secondary: #4d4d4d;
--foreground-tertiary: #7f7f7f;
--border: #e0e0e0;
--border-light: #d0d0d0;
}
}

/* Dark mode is default */
html {
color-scheme: dark;
}

body {
background: var(--background);
background-color: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
font-family: var(--font-sans);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

/* Scrollbar Styling */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}

::-webkit-scrollbar-track {
background: var(--background-secondary);
}

::-webkit-scrollbar-thumb {
background: var(--border-light);
border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
background: var(--foreground-secondary);
}

/* Selection */
::selection {
background-color: var(--primary);
color: var(--background);
}

/* Focus visible for accessibility */
:focus-visible {
outline: 2px solid var(--primary);
outline-offset: 2px;
}

/* Smooth scrolling */
html {
scroll-behavior: smooth;
}

/* Link defaults */
a {
color: var(--primary);
text-decoration: none;
transition: color 200ms ease;
}

a:hover {
color: var(--primary-light);
}

/* Utility classes for common patterns */
.live-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}

@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}

.live-dot {
display: inline-block;
width: 8px;
height: 8px;
background-color: var(--accent-green);
border-radius: 50%;
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
19 changes: 16 additions & 3 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,19 @@ const geistMono = Geist_Mono({
});

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "Swale — Football Intelligence Dashboard",
description:
"Real-time football scores, player analytics, and AI-powered insights across 50+ leagues and international tournaments.",
viewport: "width=device-width, initial-scale=1, maximum-scale=5",
keywords: [
"football",
"soccer",
"live scores",
"player stats",
"world cup",
"analytics",
"xG",
],
};

export default function RootLayout({
Expand All @@ -27,7 +38,9 @@ export default function RootLayout({
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">{children}</body>
<body className="h-full bg-[var(--background)] text-[var(--foreground)]">
{children}
</body>
</html>
);
}
109 changes: 109 additions & 0 deletions src/lib/api-football/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* API-Football Client
* Typed fetcher for API-Football (api-football.com) endpoints
* Used for live scores, player stats, and match data
*/

import { Match, MatchStatus, Team, Player, Competition, CompetitionCode } from "@/types";

Check warning on line 7 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'CompetitionCode' is defined but never used

Check warning on line 7 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'Competition' is defined but never used

Check warning on line 7 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'Team' is defined but never used

Check warning on line 7 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'MatchStatus' is defined but never used

Check warning on line 7 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'CompetitionCode' is defined but never used

Check warning on line 7 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'Competition' is defined but never used

Check warning on line 7 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'Team' is defined but never used

Check warning on line 7 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'MatchStatus' is defined but never used

Check warning on line 7 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'CompetitionCode' is defined but never used

Check warning on line 7 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'Competition' is defined but never used

Check warning on line 7 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'Team' is defined but never used

Check warning on line 7 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'MatchStatus' is defined but never used

const API_FOOTBALL_BASE = "https://api.api-football.com/v3";
const API_FOOTBALL_KEY = process.env.API_FOOTBALL_KEY;

if (!API_FOOTBALL_KEY) {
console.warn("API_FOOTBALL_KEY is not set in environment variables");
}

interface APIFootballResponse<T> {
get: string;
parameters: Record<string, unknown>;
errors: Record<string, unknown>;
results: number;
paging: {
current: number;
total: number;
};
response: T[];
}

class APIFootballClient {
private baseUrl = API_FOOTBALL_BASE;
private apiKey = API_FOOTBALL_KEY;

private async request<T>(endpoint: string, params?: Record<string, unknown>): Promise<T[]> {
if (!this.apiKey) {
console.error("API_FOOTBALL_KEY is required");
return [];
}

const url = new URL(`${this.baseUrl}${endpoint}`);
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
url.searchParams.append(key, String(value));
}
});
}

try {
const response = await fetch(url.toString(), {
headers: {
"x-apisports-key": this.apiKey,
},
});

if (!response.ok) {
throw new Error(`API-Football error: ${response.statusText}`);
}

const data = (await response.json()) as APIFootballResponse<T>;
return data.response;
} catch (error) {
console.error("API-Football request failed:", error);
throw error;
}
}

/**
* Get fixtures (matches) for a specific date or date range
*/
async getFixtures(params?: {

Check warning on line 69 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'params' is defined but never used

Check warning on line 69 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'params' is defined but never used

Check warning on line 69 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'params' is defined but never used
date?: string; // YYYY-MM-DD
dateFrom?: string;
dateTo?: string;
league?: number;
season?: number;
team?: number;
status?: string; // LIVE, FT, etc.
limit?: number;
}): Promise<Match[]> {
// This is a mock implementation since we don't have real API key
// In production, this would call the actual API-Football endpoint
return [];
}

/**
* Get player information with stats
*/
async getPlayer(playerId: number): Promise<Player | null> {

Check warning on line 87 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'playerId' is defined but never used

Check warning on line 87 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'playerId' is defined but never used

Check warning on line 87 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'playerId' is defined but never used
// Mock implementation
return null;
}

/**
* Get standings for a league
*/
async getStandings(leagueId: number, season: number) {

Check warning on line 95 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'season' is defined but never used

Check warning on line 95 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'leagueId' is defined but never used

Check warning on line 95 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'season' is defined but never used

Check warning on line 95 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'leagueId' is defined but never used

Check warning on line 95 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'season' is defined but never used

Check warning on line 95 in src/lib/api-football/client.ts

View workflow job for this annotation

GitHub Actions / build

'leagueId' is defined but never used
// Mock implementation
return [];
}

/**
* Get live matches across all leagues
*/
async getLiveMatches(): Promise<Match[]> {
// Mock implementation
return [];
}
}

export const apiFootballClient = new APIFootballClient();
Loading
Loading