diff --git a/src/App.jsx b/src/App.jsx
index f49604f..767d019 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,24 +1,25 @@
import { useState, useEffect, useCallback } from 'react'
+import CompetitionHome from './components/CompetitionHome'
import StandingsTable from './components/StandingsTable'
import TeamMatchesModal from './components/TeamMatchesModal'
-import CompetitionPicker from './components/CompetitionPicker'
+import COMPETITIONS from './competitions'
import { apiFetch } from './api'
import styles from './App.module.css'
export default function App() {
- const [selectedCode, setSelectedCode] = useState('DED')
+ const [page, setPage] = useState('home') // 'home' | 'standings'
+ const [selectedCode, setSelectedCode] = useState(null)
const [standings, setStandings] = useState(null)
const [competition, setCompetition] = useState(null)
const [season, setSeason] = useState(null)
- const [loading, setLoading] = useState(true)
+ const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
const [selectedTeam, setSelectedTeam] = useState(null)
- // Cache competition emblems keyed by code so the picker can show them
const [emblems, setEmblems] = useState({})
const closeModal = useCallback(() => setSelectedTeam(null), [])
- // Load all competition emblems once on mount so the picker shows logos immediately
+ // Load all competition emblems once on mount
useEffect(() => {
apiFetch('/competitions')
.then((data) => {
@@ -28,10 +29,12 @@ export default function App() {
}
setEmblems(map)
})
- .catch(() => {}) // non-critical — picker still works without logos
+ .catch(() => {})
}, [])
+ // Fetch standings when a competition is selected
useEffect(() => {
+ if (!selectedCode) return
setLoading(true)
setError(null)
setStandings(null)
@@ -51,29 +54,46 @@ export default function App() {
})
}, [selectedCode])
+ function handleSelect(code) {
+ setSelectedCode(code)
+ setPage('standings')
+ }
+
+ function handleBack() {
+ setPage('home')
+ setSelectedTeam(null)
+ }
+
+ if (page === 'home') {
+ return
+ }
+
+ const compMeta = COMPETITIONS.find((c) => c.code === selectedCode)
+
return (
-
-
{loading && }
{error && }
@@ -82,7 +102,7 @@ export default function App() {
)}
{!loading && !error && standings?.length === 0 && (
- Geen standen beschikbaar voor deze competitie.
+ No standings available for this competition.
)}
@@ -120,7 +140,7 @@ function Spinner() {
}}
/>
-
Laden…
+
Loading…
)
}
@@ -138,7 +158,7 @@ function ErrorMessage({ message }) {
margin: '2rem auto',
}}
>
- Fout: {message}
+ Error: {message}
)
}
diff --git a/src/App.module.css b/src/App.module.css
index 80e55e9..398f682 100644
--- a/src/App.module.css
+++ b/src/App.module.css
@@ -33,6 +33,24 @@
margin-top: 0.25rem;
}
+.backBtn {
+ background: none;
+ border: 1px solid #2a2a2a;
+ border-radius: 8px;
+ color: #ccc;
+ font-size: 1.2rem;
+ cursor: pointer;
+ padding: 0.4rem 0.75rem;
+ line-height: 1;
+ flex-shrink: 0;
+ transition: border-color 0.15s, color 0.15s;
+}
+
+.backBtn:hover {
+ border-color: #e87722;
+ color: #fff;
+}
+
.main {
width: 100%;
}
diff --git a/src/components/CompetitionHome.jsx b/src/components/CompetitionHome.jsx
new file mode 100644
index 0000000..01f68b7
--- /dev/null
+++ b/src/components/CompetitionHome.jsx
@@ -0,0 +1,32 @@
+import COMPETITIONS from '../competitions'
+import styles from './CompetitionHome.module.css'
+
+export default function CompetitionHome({ emblems, onSelect }) {
+ return (
+
+
+
+
+ {COMPETITIONS.map((comp) => (
+
+ ))}
+
+
+ )
+}
diff --git a/src/components/CompetitionHome.module.css b/src/components/CompetitionHome.module.css
new file mode 100644
index 0000000..5bc1c15
--- /dev/null
+++ b/src/components/CompetitionHome.module.css
@@ -0,0 +1,132 @@
+.page {
+ min-height: 100vh;
+ padding: 3rem 1.5rem 4rem;
+ max-width: 960px;
+ margin: 0 auto;
+}
+
+.header {
+ text-align: center;
+ margin-bottom: 3rem;
+}
+
+.title {
+ font-size: 2.25rem;
+ font-weight: 800;
+ color: #fff;
+ letter-spacing: -0.03em;
+ margin-bottom: 0.5rem;
+}
+
+.subtitle {
+ font-size: 1rem;
+ color: #666;
+}
+
+/* Tile grid */
+.grid {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 1rem;
+}
+
+.tile {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 2rem 1rem;
+ background: #161616;
+ border: 1px solid #242424;
+ border-radius: 12px;
+ cursor: pointer;
+ transition: transform 0.15s, border-color 0.15s, background 0.15s;
+ text-align: center;
+}
+
+.tile:hover {
+ background: #1e1e1e;
+ border-color: #e87722;
+ transform: translateY(-3px);
+}
+
+.emblemWrap {
+ width: 72px;
+ height: 72px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.emblem {
+ width: 72px;
+ height: 72px;
+ object-fit: contain;
+}
+
+.emblemPlaceholder {
+ width: 72px;
+ height: 72px;
+ background: #2a2a2a;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.75rem;
+ font-weight: 700;
+ color: #555;
+}
+
+.name {
+ font-size: 0.95rem;
+ font-weight: 700;
+ color: #fff;
+ line-height: 1.2;
+}
+
+.country {
+ font-size: 0.78rem;
+ color: #666;
+}
+
+/* Tablet */
+@media (max-width: 700px) {
+ .grid {
+ grid-template-columns: repeat(3, 1fr);
+ }
+
+ .title {
+ font-size: 1.75rem;
+ }
+}
+
+/* Mobile */
+@media (max-width: 480px) {
+ .page {
+ padding: 2rem 1rem 3rem;
+ }
+
+ .grid {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 0.75rem;
+ }
+
+ .title {
+ font-size: 1.5rem;
+ }
+
+ .tile {
+ padding: 1.5rem 0.75rem;
+ }
+
+ .emblemWrap,
+ .emblem,
+ .emblemPlaceholder {
+ width: 52px;
+ height: 52px;
+ }
+
+ .name {
+ font-size: 0.82rem;
+ }
+}