Skip to content

Commit a6f2624

Browse files
authored
Merge pull request #146 from RustyRory/feat/138-dashboard-membres-clean
feat(dashboard): Fixes #138 - Dashboard membres et teams
2 parents 5f7bd3e + 1d6c7be commit a6f2624

14 files changed

Lines changed: 276 additions & 67 deletions

File tree

.gitattributes

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
* text=auto eol=lf
2+
*.js text eol=lf
3+
*.ts text eol=lf
4+
*.tsx text eol=lf
5+
*.json text eol=lf
6+
*.md text eol=lf
7+
*.yml text eol=lf
8+
*.yaml text eol=lf

.github/workflows/ticket.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@ jobs:
2424
exit 0
2525
fi
2626
27-
# Regex pour le commit / titre PR : type(nom): Fixes #<num> - message
28-
if [[ "$PR_TITLE" =~ ^(feat|feature|fix|docs|chore|refactor|test|hotfix)\([a-zA-Z0-9_-]+\):\ Fixes\ \#[0-9]+\ -\ .+ ]]; then
27+
# Regex pour le titre PR : type(nom): Fixes #<num> - message
28+
TITLE_REGEX='^(feat|feature|fix|docs|chore|refactor|test|hotfix)\([a-zA-Z0-9_-]+\): Fixes #[0-9]+ - .+'
29+
if [[ "$PR_TITLE" =~ $TITLE_REGEX ]]; then
2930
echo "✅ Titre de PR valide avec référence au ticket"
3031
exit 0
3132
fi
3233
33-
# Regex pour le nom de branche : feature/123-description, fix/123-description, hotfix/123-description
34-
if [[ "$BRANCH_NAME" =~ ^(feature|fix|hotfix)/[0-9]+-.+ ]]; then
34+
# Regex pour le nom de branche : feat/123-description, feature/123-description, fix/123-description, hotfix/123-description
35+
if [[ "$BRANCH_NAME" =~ ^(feat|feature|fix|hotfix)/[0-9]+-.+ ]]; then
3536
echo "✅ Nom de branche valide avec référence au ticket"
3637
exit 0
3738
fi

saintBarthVolleyApp/backend/server.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ const PORT = process.env.PORT || 5000;
99
const MONGO_URI = process.env.MONGO_URI || 'mongodb://127.0.0.1:27017/saintbarthvolley';
1010

1111
// Connexion MongoDB
12-
mongoose.connect(MONGO_URI)
12+
mongoose
13+
.connect(MONGO_URI)
1314
.then(() => {
1415
console.log('MongoDB connected');
1516

@@ -18,4 +19,4 @@ mongoose.connect(MONGO_URI)
1819
console.log(`Server launched on http://localhost:${PORT}`);
1920
});
2021
})
21-
.catch(err => console.error('Error MongoDB:', err));
22+
.catch((err) => console.error('Error MongoDB:', err));
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// const path = require('path')
2-
2+
33
// const buildEslintCommand = (filenames) =>
44
// `eslint --fix ${filenames
55
// .map((f) => `"${path.relative(process.cwd(), f)}"`)
66
// .join(' ')}`
7-
7+
88
// module.exports = {
99
// '*.{js,jsx,ts,tsx}': [buildEslintCommand],
10-
// }
10+
// }

saintBarthVolleyApp/frontend/middleware.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ export function middleware(req: NextRequest) {
1717

1818
export const config = {
1919
matcher: ["/admin/:path*"],
20-
};
20+
};

saintBarthVolleyApp/frontend/src/app/admin/page.tsx

Lines changed: 93 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ interface Stats {
1919
activePartners: number;
2020
}
2121

22+
interface ScrapingResult {
23+
matchesCreated: number;
24+
matchesUpdated: number;
25+
standings: number;
26+
errors: string[];
27+
logs: string[];
28+
}
29+
2230
interface QuickLink {
2331
label: string;
2432
href: string;
@@ -46,11 +54,6 @@ const QUICK_LINKS: QuickLink[] = [
4654
href: "/admin/partners",
4755
description: "Gérer les sponsors et partenaires",
4856
},
49-
{
50-
label: "Matches & scraping",
51-
href: "/admin/matches",
52-
description: "Voir les matches et lancer le scraping FFVB",
53-
},
5457
{
5558
label: "Infos du club",
5659
href: "/admin/club",
@@ -62,27 +65,101 @@ export default function AdminDashboardPage() {
6265
const [stats, setStats] = React.useState<Stats | null>(null);
6366
const [loading, setLoading] = React.useState(true);
6467

68+
const [scraping, setScraping] = React.useState(false);
69+
const [scrapingResult, setScrapingResult] =
70+
React.useState<ScrapingResult | null>(null);
71+
const [scrapingError, setScrapingError] = React.useState<string | null>(null);
72+
const [showLogs, setShowLogs] = React.useState(false);
73+
6574
React.useEffect(() => {
66-
apiFetch("/api/stats")
75+
apiFetch<Stats>("/api/stats")
6776
.then(setStats)
6877
.catch(console.error)
6978
.finally(() => setLoading(false));
7079
}, []);
7180

81+
const handleScraping = async () => {
82+
if (
83+
!confirm("Lancer le scraping FFVB ? Cela peut prendre plusieurs minutes.")
84+
)
85+
return;
86+
setScraping(true);
87+
setScrapingResult(null);
88+
setScrapingError(null);
89+
try {
90+
const result = await apiFetch<ScrapingResult>("/api/scraping/run", {
91+
method: "POST",
92+
});
93+
setScrapingResult(result);
94+
// Refresh stats after scraping
95+
const updated = await apiFetch<Stats>("/api/stats").catch(() => null);
96+
if (updated) setStats(updated);
97+
} catch (err) {
98+
setScrapingError(
99+
err instanceof Error ? err.message : "Erreur lors du scraping",
100+
);
101+
} finally {
102+
setScraping(false);
103+
}
104+
};
105+
72106
return (
73107
<div className="flex flex-1 flex-col gap-8">
74-
<div>
75-
<h1 className="text-2xl font-bold">Dashboard</h1>
76-
{stats?.activeSeason && (
77-
<p className="text-sm text-muted-foreground mt-1">
78-
Saison active :{" "}
79-
<span className="font-medium text-foreground">
80-
{stats.activeSeason}
81-
</span>
82-
</p>
83-
)}
108+
<div className="flex items-start justify-between gap-4 flex-wrap">
109+
<div>
110+
<h1 className="text-2xl font-bold">Dashboard</h1>
111+
{stats?.activeSeason && (
112+
<p className="text-sm text-muted-foreground mt-1">
113+
Saison active :{" "}
114+
<span className="font-medium text-foreground">
115+
{stats.activeSeason}
116+
</span>
117+
</p>
118+
)}
119+
</div>
120+
121+
<Button
122+
onClick={handleScraping}
123+
disabled={scraping}
124+
className="bg-orange-600 hover:bg-orange-700 text-white shrink-0"
125+
>
126+
{scraping ? "⟳ Scraping en cours..." : "Lancer le scraping FFVB"}
127+
</Button>
84128
</div>
85129

130+
{/* Résultat scraping */}
131+
{scrapingResult && (
132+
<div className="border rounded-lg p-4 bg-green-50 border-green-200 flex flex-col gap-2">
133+
<div className="font-semibold text-green-800">Scraping terminé</div>
134+
<div className="text-sm text-green-700 flex flex-wrap gap-4">
135+
<span>{scrapingResult.matchesCreated} match(es) créé(s)</span>
136+
<span>{scrapingResult.matchesUpdated} mis à jour</span>
137+
<span>{scrapingResult.standings} classement(s)</span>
138+
</div>
139+
{scrapingResult.errors.filter(Boolean).length > 0 && (
140+
<div className="text-sm text-red-600">
141+
Erreurs : {scrapingResult.errors.join(", ")}
142+
</div>
143+
)}
144+
<button
145+
className="text-xs text-green-600 underline self-start"
146+
onClick={() => setShowLogs((v) => !v)}
147+
>
148+
{showLogs ? "Masquer les logs" : "Voir les logs"}
149+
</button>
150+
{showLogs && (
151+
<pre className="text-xs bg-white border rounded p-3 overflow-auto max-h-48 whitespace-pre-wrap">
152+
{scrapingResult.logs.join("\n")}
153+
</pre>
154+
)}
155+
</div>
156+
)}
157+
{scrapingError && (
158+
<div className="border rounded-lg p-4 bg-red-50 border-red-200 text-red-700 text-sm">
159+
{scrapingError}
160+
</div>
161+
)}
162+
86163
<SectionAdminCards stats={stats} loading={loading} />
87164

88165
{/* Accès rapides */}

saintBarthVolleyApp/frontend/src/app/admin/partners/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ export default function PartnersPage() {
150150
>
151151
<div className="flex items-center gap-3">
152152
{p.logo ? (
153+
// eslint-disable-next-line @next/next/no-img-element
153154
<img
154155
src={p.logo}
155156
alt={p.name}
@@ -256,6 +257,7 @@ export default function PartnersPage() {
256257
placeholder="https://example.com/logo.png"
257258
/>
258259
{editing.logo && (
260+
// eslint-disable-next-line @next/next/no-img-element
259261
<img
260262
src={editing.logo}
261263
alt="Aperçu logo"

0 commit comments

Comments
 (0)