From a6cd64dd5ded96a404397918460988676704e932 Mon Sep 17 00:00:00 2001
From: ExostFlash
Date: Mon, 2 Jun 2025 09:57:48 +0200
Subject: [PATCH 01/17] Add interfaces & services
---
FencerJudgeFront/src/app/app.component.ts | 2 +-
.../essentials/header/header.component.ts | 6 +-
.../src/app/interfaces/matches.ts | 19 +++++
.../src/app/interfaces/models/user.model.ts | 5 ++
FencerJudgeFront/src/app/interfaces/player.ts | 5 ++
.../src/app/interfaces/referee.ts | 12 ++++
.../src/app/services/matches.service.spec.ts | 16 +++++
.../src/app/services/matches.service.ts | 71 +++++++++++++++++++
.../src/app/services/player.service.spec.ts | 16 +++++
.../src/app/services/player.service.ts | 30 ++++++++
.../src/app/services/referee.service.spec.ts | 16 +++++
.../src/app/services/referee.service.ts | 42 +++++++++++
FencerJudgeFront/tsconfig.json | 8 ++-
package-lock.json | 6 ++
14 files changed, 248 insertions(+), 6 deletions(-)
create mode 100644 FencerJudgeFront/src/app/interfaces/matches.ts
create mode 100644 FencerJudgeFront/src/app/interfaces/models/user.model.ts
create mode 100644 FencerJudgeFront/src/app/interfaces/player.ts
create mode 100644 FencerJudgeFront/src/app/interfaces/referee.ts
create mode 100644 FencerJudgeFront/src/app/services/matches.service.spec.ts
create mode 100644 FencerJudgeFront/src/app/services/matches.service.ts
create mode 100644 FencerJudgeFront/src/app/services/player.service.spec.ts
create mode 100644 FencerJudgeFront/src/app/services/player.service.ts
create mode 100644 FencerJudgeFront/src/app/services/referee.service.spec.ts
create mode 100644 FencerJudgeFront/src/app/services/referee.service.ts
create mode 100644 package-lock.json
diff --git a/FencerJudgeFront/src/app/app.component.ts b/FencerJudgeFront/src/app/app.component.ts
index edbb159..c713d57 100644
--- a/FencerJudgeFront/src/app/app.component.ts
+++ b/FencerJudgeFront/src/app/app.component.ts
@@ -4,7 +4,7 @@ import { Component } from '@angular/core';
selector: 'app-root',
templateUrl: './app.component.html',
standalone: false,
- styleUrl: './app.component.css'
+ styleUrl: './app.component.css',
})
export class AppComponent {
title = 'FencerJudgeFront';
diff --git a/FencerJudgeFront/src/app/components/essentials/header/header.component.ts b/FencerJudgeFront/src/app/components/essentials/header/header.component.ts
index f572069..ffc3163 100644
--- a/FencerJudgeFront/src/app/components/essentials/header/header.component.ts
+++ b/FencerJudgeFront/src/app/components/essentials/header/header.component.ts
@@ -4,8 +4,6 @@ import { Component } from '@angular/core';
selector: 'app-header',
standalone: false,
templateUrl: './header.component.html',
- styleUrl: './header.component.css'
+ styleUrl: './header.component.css',
})
-export class HeaderComponent {
-
-}
+export class HeaderComponent {}
diff --git a/FencerJudgeFront/src/app/interfaces/matches.ts b/FencerJudgeFront/src/app/interfaces/matches.ts
new file mode 100644
index 0000000..26c6767
--- /dev/null
+++ b/FencerJudgeFront/src/app/interfaces/matches.ts
@@ -0,0 +1,19 @@
+export enum MatchState {
+ ONGOING = 1,
+ OVER = 2,
+ NOT_STARTED = 3,
+}
+
+export interface Matches {
+ id: number;
+ refereeID: number;
+ player1ID: number;
+ score1: number;
+ player2ID: number;
+ score2: number;
+ country: String;
+ city: String;
+ weapon: String;
+ date: Date;
+ state: MatchState;
+}
diff --git a/FencerJudgeFront/src/app/interfaces/models/user.model.ts b/FencerJudgeFront/src/app/interfaces/models/user.model.ts
new file mode 100644
index 0000000..954ae9a
--- /dev/null
+++ b/FencerJudgeFront/src/app/interfaces/models/user.model.ts
@@ -0,0 +1,5 @@
+export interface UserModel {
+ id: number;
+ name: string;
+ firstName: string;
+}
diff --git a/FencerJudgeFront/src/app/interfaces/player.ts b/FencerJudgeFront/src/app/interfaces/player.ts
new file mode 100644
index 0000000..6f39558
--- /dev/null
+++ b/FencerJudgeFront/src/app/interfaces/player.ts
@@ -0,0 +1,5 @@
+import { UserModel } from './models/user.model';
+
+export interface Player extends UserModel {
+ club: string;
+}
diff --git a/FencerJudgeFront/src/app/interfaces/referee.ts b/FencerJudgeFront/src/app/interfaces/referee.ts
new file mode 100644
index 0000000..3b4f0c1
--- /dev/null
+++ b/FencerJudgeFront/src/app/interfaces/referee.ts
@@ -0,0 +1,12 @@
+import { UserModel } from './models/user.model';
+
+export enum RefereeLevel {
+ DEPARTMENTAL = 0,
+ REGIONAL = 1,
+ NATIONAL = 2,
+ INTERNATIONAL = 3,
+}
+
+export interface Referee extends UserModel {
+ level: RefereeLevel;
+}
diff --git a/FencerJudgeFront/src/app/services/matches.service.spec.ts b/FencerJudgeFront/src/app/services/matches.service.spec.ts
new file mode 100644
index 0000000..0e9bf15
--- /dev/null
+++ b/FencerJudgeFront/src/app/services/matches.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { MatchesService } from './matches.service';
+
+describe('MatchesService', () => {
+ let service: MatchesService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(MatchesService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/FencerJudgeFront/src/app/services/matches.service.ts b/FencerJudgeFront/src/app/services/matches.service.ts
new file mode 100644
index 0000000..12a6201
--- /dev/null
+++ b/FencerJudgeFront/src/app/services/matches.service.ts
@@ -0,0 +1,71 @@
+import { Injectable } from '@angular/core';
+import { Observable, of } from 'rxjs';
+import { Matches, MatchState } from '@interfaces/matches';
+
+@Injectable({ providedIn: 'root' })
+export class MatchesService {
+ private matches: Matches[] = [
+ {
+ id: 1,
+ refereeID: 10,
+ player1ID: 1,
+ score1: 15,
+ player2ID: 2,
+ score2: 13,
+ country: 'France',
+ city: 'Paris',
+ weapon: 'Fleuret',
+ date: new Date('2025-06-10T14:00:00'),
+ state: MatchState.OVER,
+ },
+ {
+ id: 2,
+ refereeID: 11,
+ player1ID: 3,
+ score1: 5,
+ player2ID: 4,
+ score2: 7,
+ country: 'France',
+ city: 'Lyon',
+ weapon: 'Épée',
+ date: new Date('2025-06-15T10:00:00'),
+ state: MatchState.ONGOING,
+ },
+ {
+ id: 3,
+ refereeID: 12,
+ player1ID: 5,
+ score1: 0,
+ player2ID: 6,
+ score2: 0,
+ country: 'Belgique',
+ city: 'Bruxelles',
+ weapon: 'Sabre',
+ date: new Date('2025-06-20T16:30:00'),
+ state: MatchState.NOT_STARTED,
+ },
+ ];
+
+ constructor() {
+ console.log('[MatchesService] Initial matches loaded:', this.matches);
+ }
+
+ getMatches(): Observable {
+ console.log('[MatchesService] Fetching all matches');
+ return of(this.matches);
+ }
+
+ getMatchById(id: number): Observable {
+ const match = this.matches.find((m) => m.id === id);
+ console.log(`[MatchesService] Fetching match ID: ${id}`, match);
+ return of(match);
+ }
+
+ // Préparation future pour WebSocket
+ connectToMatchUpdatesWebSocket(): void {
+ console.log(
+ '[MatchesService] WebSocket connection placeholder initialized'
+ );
+ // ici tu pourrais plus tard faire : this.socket = new WebSocket('ws://...') etc.
+ }
+}
diff --git a/FencerJudgeFront/src/app/services/player.service.spec.ts b/FencerJudgeFront/src/app/services/player.service.spec.ts
new file mode 100644
index 0000000..5355445
--- /dev/null
+++ b/FencerJudgeFront/src/app/services/player.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { PlayerService } from './player.service';
+
+describe('PlayerService', () => {
+ let service: PlayerService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(PlayerService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/FencerJudgeFront/src/app/services/player.service.ts b/FencerJudgeFront/src/app/services/player.service.ts
new file mode 100644
index 0000000..53d4350
--- /dev/null
+++ b/FencerJudgeFront/src/app/services/player.service.ts
@@ -0,0 +1,30 @@
+import { Injectable } from '@angular/core';
+import { Observable, of } from 'rxjs';
+import { Player } from '@interfaces/player';
+
+@Injectable({ providedIn: 'root' })
+export class PlayerService {
+ private players: Player[] = [
+ { id: 1, name: 'Martin', firstName: 'Alex', club: 'Lille Escrime' },
+ { id: 2, name: 'Nguyen', firstName: 'Sophie', club: 'Paris Epée' },
+ { id: 3, name: 'Klein', firstName: 'Thomas', club: 'Strasbourg Sabre' },
+ { id: 4, name: 'Leclerc', firstName: 'Lucie', club: 'Nice Fleuret' },
+ { id: 5, name: 'Dubois', firstName: 'Hugo', club: 'Lyon Epée' },
+ { id: 6, name: 'Girard', firstName: 'Manon', club: 'Marseille Club' },
+ ];
+
+ constructor() {
+ console.log('[PlayerService] Initial players loaded:', this.players);
+ }
+
+ getPlayers(): Observable {
+ console.log('[PlayerService] Fetching all players');
+ return of(this.players);
+ }
+
+ getPlayerById(id: number): Observable {
+ const player = this.players.find((p) => p.id === id);
+ console.log(`[PlayerService] Fetching player ID: ${id}`, player);
+ return of(player);
+ }
+}
diff --git a/FencerJudgeFront/src/app/services/referee.service.spec.ts b/FencerJudgeFront/src/app/services/referee.service.spec.ts
new file mode 100644
index 0000000..b0f470c
--- /dev/null
+++ b/FencerJudgeFront/src/app/services/referee.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { RefereeService } from './referee.service';
+
+describe('RefereeService', () => {
+ let service: RefereeService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(RefereeService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/FencerJudgeFront/src/app/services/referee.service.ts b/FencerJudgeFront/src/app/services/referee.service.ts
new file mode 100644
index 0000000..6963230
--- /dev/null
+++ b/FencerJudgeFront/src/app/services/referee.service.ts
@@ -0,0 +1,42 @@
+import { Injectable } from '@angular/core';
+import { Observable, of } from 'rxjs';
+import { Referee, RefereeLevel } from '@interfaces/referee';
+
+@Injectable({ providedIn: 'root' })
+export class RefereeService {
+ private referees: Referee[] = [
+ {
+ id: 10,
+ name: 'Durand',
+ firstName: 'Pierre',
+ level: RefereeLevel.NATIONAL,
+ },
+ {
+ id: 11,
+ name: 'Lemoine',
+ firstName: 'Anna',
+ level: RefereeLevel.REGIONAL,
+ },
+ {
+ id: 12,
+ name: 'Morel',
+ firstName: 'Lucas',
+ level: RefereeLevel.DEPARTMENTAL,
+ },
+ ];
+
+ constructor() {
+ console.log('[RefereeService] Initial referees loaded:', this.referees);
+ }
+
+ getReferees(): Observable {
+ console.log('[RefereeService] Fetching all referees');
+ return of(this.referees);
+ }
+
+ getRefereeById(id: number): Observable {
+ const ref = this.referees.find((r) => r.id === id);
+ console.log(`[RefereeService] Fetching referee ID: ${id}`, ref);
+ return of(ref);
+ }
+}
diff --git a/FencerJudgeFront/tsconfig.json b/FencerJudgeFront/tsconfig.json
index 5525117..06d41df 100644
--- a/FencerJudgeFront/tsconfig.json
+++ b/FencerJudgeFront/tsconfig.json
@@ -16,7 +16,13 @@
"moduleResolution": "bundler",
"importHelpers": true,
"target": "ES2022",
- "module": "ES2022"
+ "module": "ES2022",
+ "baseUrl": "./src",
+ "paths": {
+ "@interfaces/*": ["app/interfaces/*"],
+ "@services/*": ["app/services/*"],
+ "@utils/*": ["app/utils/*"]
+ }
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..966ef08
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,6 @@
+{
+ "name": "jpe-controle",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {}
+}
--
2.45.3
From b787a136699cdb6e7609b245e50429d9ea46e5c5 Mon Sep 17 00:00:00 2001
From: ExostFlash
Date: Mon, 2 Jun 2025 10:36:29 +0200
Subject: [PATCH 02/17] Add @guards on the tsconfig.json
---
FencerJudgeFront/src/app/app-routing.module.ts | 2 +-
FencerJudgeFront/tsconfig.json | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/FencerJudgeFront/src/app/app-routing.module.ts b/FencerJudgeFront/src/app/app-routing.module.ts
index 45896b5..0f8009a 100644
--- a/FencerJudgeFront/src/app/app-routing.module.ts
+++ b/FencerJudgeFront/src/app/app-routing.module.ts
@@ -1,7 +1,7 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
-import { authGuard } from './guards/auth.guard';
+import { authGuard } from '@guards/auth.guard';
import { LoginComponent } from './components/essentials/login/login.component';
import { HomeComponent } from './components/home/home.component';
diff --git a/FencerJudgeFront/tsconfig.json b/FencerJudgeFront/tsconfig.json
index 06d41df..fca3db7 100644
--- a/FencerJudgeFront/tsconfig.json
+++ b/FencerJudgeFront/tsconfig.json
@@ -21,6 +21,7 @@
"paths": {
"@interfaces/*": ["app/interfaces/*"],
"@services/*": ["app/services/*"],
+ "@guards/*": ["app/guards/*"],
"@utils/*": ["app/utils/*"]
}
},
--
2.45.3
From e05f356beef1a7a6c329291617b7a6b2d97dd06c Mon Sep 17 00:00:00 2001
From: ExostFlash
Date: Mon, 2 Jun 2025 13:25:51 +0200
Subject: [PATCH 03/17] modif
---
.../src/app/app-routing.module.ts | 2 +
.../essentials/footer/footer.component.html | 59 ++++++++++++++++++-
.../essentials/header/header.component.html | 46 ++++++++++++++-
.../essentials/header/header.component.ts | 18 +++++-
.../essentials/logout/logout.component.ts | 9 ++-
FencerJudgeFront/src/styles.css | 3 +
6 files changed, 132 insertions(+), 5 deletions(-)
diff --git a/FencerJudgeFront/src/app/app-routing.module.ts b/FencerJudgeFront/src/app/app-routing.module.ts
index 0f8009a..f88972f 100644
--- a/FencerJudgeFront/src/app/app-routing.module.ts
+++ b/FencerJudgeFront/src/app/app-routing.module.ts
@@ -4,10 +4,12 @@ import { RouterModule, Routes } from '@angular/router';
import { authGuard } from '@guards/auth.guard';
import { LoginComponent } from './components/essentials/login/login.component';
+import { LogoutComponent } from './components/essentials/logout/logout.component';
import { HomeComponent } from './components/home/home.component';
const routes: Routes = [
{ path: 'login', component: LoginComponent },
+ { path: 'logout', component: LogoutComponent },
{ path: '', component: HomeComponent },
{ path: 'home', component: HomeComponent },
];
diff --git a/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html b/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html
index 28c0d7d..d280498 100644
--- a/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html
+++ b/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html
@@ -1 +1,58 @@
-footer works!
+
diff --git a/FencerJudgeFront/src/app/components/essentials/header/header.component.html b/FencerJudgeFront/src/app/components/essentials/header/header.component.html
index 4f5a95d..b68cf30 100644
--- a/FencerJudgeFront/src/app/components/essentials/header/header.component.html
+++ b/FencerJudgeFront/src/app/components/essentials/header/header.component.html
@@ -1 +1,45 @@
-header works!
+
+
diff --git a/FencerJudgeFront/src/app/components/essentials/header/header.component.ts b/FencerJudgeFront/src/app/components/essentials/header/header.component.ts
index ffc3163..8d32d92 100644
--- a/FencerJudgeFront/src/app/components/essentials/header/header.component.ts
+++ b/FencerJudgeFront/src/app/components/essentials/header/header.component.ts
@@ -1,4 +1,6 @@
import { Component } from '@angular/core';
+import { Router } from '@angular/router';
+import { AuthService } from '@services/auth.service';
@Component({
selector: 'app-header',
@@ -6,4 +8,18 @@ import { Component } from '@angular/core';
templateUrl: './header.component.html',
styleUrl: './header.component.css',
})
-export class HeaderComponent {}
+export class HeaderComponent {
+ constructor(private router: Router, public authService: AuthService) {}
+
+ isHomeActive(): boolean {
+ return this.router.url === '/' || this.router.url === '/list';
+ }
+
+ isHomeActiveBool(): boolean {
+ if (this.router.url != '/list') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/FencerJudgeFront/src/app/components/essentials/logout/logout.component.ts b/FencerJudgeFront/src/app/components/essentials/logout/logout.component.ts
index f8b8892..6da5b61 100644
--- a/FencerJudgeFront/src/app/components/essentials/logout/logout.component.ts
+++ b/FencerJudgeFront/src/app/components/essentials/logout/logout.component.ts
@@ -1,11 +1,16 @@
import { Component } from '@angular/core';
+import { Router } from '@angular/router';
+import { AuthService } from '@services/auth.service';
@Component({
selector: 'app-logout',
standalone: false,
templateUrl: './logout.component.html',
- styleUrl: './logout.component.css'
+ styleUrl: './logout.component.css',
})
export class LogoutComponent {
-
+ constructor(private authService: AuthService, private router: Router) {
+ this.authService.logout();
+ this.router.navigate(['/']); // Redirection après déconnexion
+ }
}
diff --git a/FencerJudgeFront/src/styles.css b/FencerJudgeFront/src/styles.css
index 90d4ee0..c44aac6 100644
--- a/FencerJudgeFront/src/styles.css
+++ b/FencerJudgeFront/src/styles.css
@@ -1 +1,4 @@
/* You can add global styles to this file, and also import other style files */
+.bg-primary-custom {
+ background-color: #414141;
+}
--
2.45.3
From f61416c4d0989068efd70c62312a8ad59d0c7f4e Mon Sep 17 00:00:00 2001
From: ExostFlash
Date: Mon, 2 Jun 2025 14:06:26 +0200
Subject: [PATCH 04/17] Add list matchs
---
.../src/app/app-routing.module.ts | 2 +
FencerJudgeFront/src/app/app.module.ts | 2 +
.../components/matches/matches.component.css | 8 +++
.../components/matches/matches.component.html | 51 ++++++++++++++
.../matches/matches.component.spec.ts | 23 +++++++
.../components/matches/matches.component.ts | 66 +++++++++++++++++++
6 files changed, 152 insertions(+)
create mode 100644 FencerJudgeFront/src/app/components/matches/matches.component.css
create mode 100644 FencerJudgeFront/src/app/components/matches/matches.component.html
create mode 100644 FencerJudgeFront/src/app/components/matches/matches.component.spec.ts
create mode 100644 FencerJudgeFront/src/app/components/matches/matches.component.ts
diff --git a/FencerJudgeFront/src/app/app-routing.module.ts b/FencerJudgeFront/src/app/app-routing.module.ts
index f88972f..5bf8bf7 100644
--- a/FencerJudgeFront/src/app/app-routing.module.ts
+++ b/FencerJudgeFront/src/app/app-routing.module.ts
@@ -6,12 +6,14 @@ import { authGuard } from '@guards/auth.guard';
import { LoginComponent } from './components/essentials/login/login.component';
import { LogoutComponent } from './components/essentials/logout/logout.component';
import { HomeComponent } from './components/home/home.component';
+import { MatchesComponent } from './components/matches/matches.component';
const routes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: 'logout', component: LogoutComponent },
{ path: '', component: HomeComponent },
{ path: 'home', component: HomeComponent },
+ { path: 'matches', component: MatchesComponent },
];
@NgModule({
diff --git a/FencerJudgeFront/src/app/app.module.ts b/FencerJudgeFront/src/app/app.module.ts
index 1a172c4..a3dd923 100644
--- a/FencerJudgeFront/src/app/app.module.ts
+++ b/FencerJudgeFront/src/app/app.module.ts
@@ -9,6 +9,7 @@ import { FooterComponent } from './components/essentials/footer/footer.component
import { HomeComponent } from './components/home/home.component';
import { LoginComponent } from './components/essentials/login/login.component';
import { LogoutComponent } from './components/essentials/logout/logout.component';
+import { MatchesComponent } from './components/matches/matches.component';
@NgModule({
declarations: [
@@ -18,6 +19,7 @@ import { LogoutComponent } from './components/essentials/logout/logout.component
HomeComponent,
LoginComponent,
LogoutComponent,
+ MatchesComponent,
],
imports: [BrowserModule, AppRoutingModule, FormsModule],
providers: [],
diff --git a/FencerJudgeFront/src/app/components/matches/matches.component.css b/FencerJudgeFront/src/app/components/matches/matches.component.css
new file mode 100644
index 0000000..2fa4ff6
--- /dev/null
+++ b/FencerJudgeFront/src/app/components/matches/matches.component.css
@@ -0,0 +1,8 @@
+.border-start-5 {
+ border-left: 5px solid #ccc;
+}
+
+.card:hover {
+ transform: translateY(-2px);
+ transition: 0.2s ease-in-out;
+}
diff --git a/FencerJudgeFront/src/app/components/matches/matches.component.html b/FencerJudgeFront/src/app/components/matches/matches.component.html
new file mode 100644
index 0000000..a5561bb
--- /dev/null
+++ b/FencerJudgeFront/src/app/components/matches/matches.component.html
@@ -0,0 +1,51 @@
+
+
Liste des Matchs
+
+
+
+
+
+
+
+ {{ match.city }} —
+ {{
+ match.date | date : "longDate"
+ }}
+
+ {{ match.weapon }}
+
+
+
+
+ {{ getPlayerName(match.player1ID) }}
+ {{ match.score1 }}
+
+
+
vs
+
+
+ {{ getPlayerName(match.player2ID) }}
+ {{ match.score2 }}
+
+
+
+
+
+ {{ getMatchStatusLabel(match.state) }}
+
+
+
+
+
+
+
diff --git a/FencerJudgeFront/src/app/components/matches/matches.component.spec.ts b/FencerJudgeFront/src/app/components/matches/matches.component.spec.ts
new file mode 100644
index 0000000..7f7c3e1
--- /dev/null
+++ b/FencerJudgeFront/src/app/components/matches/matches.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MatchesComponent } from './matches.component';
+
+describe('MatchesComponent', () => {
+ let component: MatchesComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [MatchesComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(MatchesComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/FencerJudgeFront/src/app/components/matches/matches.component.ts b/FencerJudgeFront/src/app/components/matches/matches.component.ts
new file mode 100644
index 0000000..935e3d1
--- /dev/null
+++ b/FencerJudgeFront/src/app/components/matches/matches.component.ts
@@ -0,0 +1,66 @@
+import { Component } from '@angular/core';
+
+import { MatchesService } from '@services/matches.service';
+import { Matches, MatchState } from '@interfaces/matches';
+
+import { PlayerService } from '@services/player.service';
+import { Player } from '@interfaces/player';
+
+@Component({
+ selector: 'app-matches',
+ standalone: false,
+ templateUrl: './matches.component.html',
+ styleUrl: './matches.component.css',
+})
+export class MatchesComponent {
+ matches: Matches[] = [];
+ playersMap: Map = new Map();
+ MatchState = MatchState;
+
+ constructor(
+ private matchesService: MatchesService,
+ private playerService: PlayerService
+ ) {}
+
+ ngOnInit(): void {
+ this.playerService.getPlayers().subscribe((players) => {
+ // construire la map ID -> joueur
+ players.forEach((player) => this.playersMap.set(player.id, player));
+ });
+
+ this.matchesService.getMatches().subscribe((data) => {
+ this.matches = data;
+ });
+ }
+
+ getPlayerName(id: number): string {
+ const player = this.playersMap.get(id);
+ return player ? `${player.firstName} ${player.name}` : 'Joueur inconnu';
+ }
+
+ getMatchBorderColor(state: MatchState): string {
+ switch (state) {
+ case MatchState.NOT_STARTED:
+ return 'border-secondary';
+ case MatchState.ONGOING:
+ return 'border-warning';
+ case MatchState.OVER:
+ return 'border-success';
+ default:
+ return 'border-light';
+ }
+ }
+
+ getMatchStatusLabel(state: MatchState): string {
+ switch (state) {
+ case MatchState.NOT_STARTED:
+ return 'À venir';
+ case MatchState.ONGOING:
+ return 'En cours';
+ case MatchState.OVER:
+ return 'Terminé';
+ default:
+ return 'Inconnu';
+ }
+ }
+}
--
2.45.3
From 83c6b6e504b5e8d2402636c29b150c23a3701b8c Mon Sep 17 00:00:00 2001
From: ExostFlash
Date: Mon, 2 Jun 2025 15:04:33 +0200
Subject: [PATCH 05/17] modif match
---
.../src/app/app-routing.module.ts | 2 +
FencerJudgeFront/src/app/app.module.ts | 2 +
.../matches-id/matches-id.component.css | 3 +
.../matches-id/matches-id.component.html | 94 ++++++++++++
.../matches-id/matches-id.component.spec.ts | 23 +++
.../matches-id/matches-id.component.ts | 141 ++++++++++++++++++
.../components/matches/matches.component.html | 2 +
.../components/matches/matches.component.ts | 8 +-
.../src/app/services/matches.service.ts | 10 ++
9 files changed, 284 insertions(+), 1 deletion(-)
create mode 100644 FencerJudgeFront/src/app/components/matches-id/matches-id.component.css
create mode 100644 FencerJudgeFront/src/app/components/matches-id/matches-id.component.html
create mode 100644 FencerJudgeFront/src/app/components/matches-id/matches-id.component.spec.ts
create mode 100644 FencerJudgeFront/src/app/components/matches-id/matches-id.component.ts
diff --git a/FencerJudgeFront/src/app/app-routing.module.ts b/FencerJudgeFront/src/app/app-routing.module.ts
index 5bf8bf7..fcdc89f 100644
--- a/FencerJudgeFront/src/app/app-routing.module.ts
+++ b/FencerJudgeFront/src/app/app-routing.module.ts
@@ -7,6 +7,7 @@ import { LoginComponent } from './components/essentials/login/login.component';
import { LogoutComponent } from './components/essentials/logout/logout.component';
import { HomeComponent } from './components/home/home.component';
import { MatchesComponent } from './components/matches/matches.component';
+import { MatchesIdComponent } from './components/matches-id/matches-id.component';
const routes: Routes = [
{ path: 'login', component: LoginComponent },
@@ -14,6 +15,7 @@ const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'home', component: HomeComponent },
{ path: 'matches', component: MatchesComponent },
+ { path: 'matches/:id', component: MatchesIdComponent },
];
@NgModule({
diff --git a/FencerJudgeFront/src/app/app.module.ts b/FencerJudgeFront/src/app/app.module.ts
index a3dd923..0c33646 100644
--- a/FencerJudgeFront/src/app/app.module.ts
+++ b/FencerJudgeFront/src/app/app.module.ts
@@ -10,6 +10,7 @@ import { HomeComponent } from './components/home/home.component';
import { LoginComponent } from './components/essentials/login/login.component';
import { LogoutComponent } from './components/essentials/logout/logout.component';
import { MatchesComponent } from './components/matches/matches.component';
+import { MatchesIdComponent } from './components/matches-id/matches-id.component';
@NgModule({
declarations: [
@@ -20,6 +21,7 @@ import { MatchesComponent } from './components/matches/matches.component';
LoginComponent,
LogoutComponent,
MatchesComponent,
+ MatchesIdComponent,
],
imports: [BrowserModule, AppRoutingModule, FormsModule],
providers: [],
diff --git a/FencerJudgeFront/src/app/components/matches-id/matches-id.component.css b/FencerJudgeFront/src/app/components/matches-id/matches-id.component.css
new file mode 100644
index 0000000..f517d5c
--- /dev/null
+++ b/FencerJudgeFront/src/app/components/matches-id/matches-id.component.css
@@ -0,0 +1,3 @@
+.card {
+ border-left-width: 0.5rem !important;
+}
diff --git a/FencerJudgeFront/src/app/components/matches-id/matches-id.component.html b/FencerJudgeFront/src/app/components/matches-id/matches-id.component.html
new file mode 100644
index 0000000..38721f0
--- /dev/null
+++ b/FencerJudgeFront/src/app/components/matches-id/matches-id.component.html
@@ -0,0 +1,94 @@
+
+
Détail du Match
+
+
+
+
+
+ {{ match.city }}, {{ match.country }}
+
+ {{ match.date | date : "fullDate" }} -
+ {{ match.date | date : "shortTime" }}
+
+ {{ match.weapon }}
+
+
+
+
+ {{ player1?.firstName }} {{ player1?.name }}
+ {{ player1?.club }}
+
+
+
+
+
+ {{ match.score1 }}
+
+ vs
+
+ {{ match.score2 }}
+
+
+
+
+ {{ match.score1 }}
+ vs
+ {{ match.score2 }}
+
+
+
+
+ {{ player2?.firstName }} {{ player2?.name }}
+ {{ player2?.club }}
+
+
+
+
+
+ {{ getMatchStateLabel(match.state) }}
+
+
+
+
+
Arbitre
+
+
+ {{ referee.firstName }} {{ referee.name }}
+
+
+ Niveau : {{ getRefereeLevelLabel(referee.level) }}
+
+
+
+
+
+
+
+
+
+
diff --git a/FencerJudgeFront/src/app/components/matches-id/matches-id.component.spec.ts b/FencerJudgeFront/src/app/components/matches-id/matches-id.component.spec.ts
new file mode 100644
index 0000000..b09ff2d
--- /dev/null
+++ b/FencerJudgeFront/src/app/components/matches-id/matches-id.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MatchesIdComponent } from './matches-id.component';
+
+describe('MatchesIdComponent', () => {
+ let component: MatchesIdComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [MatchesIdComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(MatchesIdComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/FencerJudgeFront/src/app/components/matches-id/matches-id.component.ts b/FencerJudgeFront/src/app/components/matches-id/matches-id.component.ts
new file mode 100644
index 0000000..2e0be56
--- /dev/null
+++ b/FencerJudgeFront/src/app/components/matches-id/matches-id.component.ts
@@ -0,0 +1,141 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { AuthService } from '@services/auth.service';
+
+import { MatchesService } from '@services/matches.service';
+import { MatchState, Matches } from '@interfaces/matches';
+import { PlayerService } from '@services/player.service';
+import { Player } from '@interfaces/player';
+import { RefereeService } from '@services/referee.service';
+import { Referee, RefereeLevel } from '@interfaces/referee';
+
+@Component({
+ selector: 'app-matches-id',
+ standalone: false,
+ templateUrl: './matches-id.component.html',
+ styleUrl: './matches-id.component.css',
+})
+export class MatchesIdComponent implements OnInit {
+ match: Matches | undefined;
+ player1: Player | undefined;
+ player2: Player | undefined;
+ referee: Referee | undefined;
+
+ constructor(
+ private route: ActivatedRoute,
+ private matchService: MatchesService,
+ private playerService: PlayerService,
+ private refereeService: RefereeService,
+ public authService: AuthService
+ ) {}
+
+ ngOnInit(): void {
+ const id = Number(this.route.snapshot.paramMap.get('id'));
+ this.matchService.getMatchById(id).subscribe((match) => {
+ if (match) {
+ this.match = match;
+
+ this.playerService
+ .getPlayerById(match.player1ID)
+ .subscribe((p1) => (this.player1 = p1!));
+ this.playerService
+ .getPlayerById(match.player2ID)
+ .subscribe((p2) => (this.player2 = p2!));
+
+ this.refereeService
+ .getRefereeById(match.refereeID)
+ .subscribe((ref) => (this.referee = ref!));
+ }
+ });
+ }
+
+ saveScores(): void {
+ if (this.match) {
+ this.matchService.updateMatch(this.match.id, {
+ score1: this.match.score1,
+ score2: this.match.score2,
+ });
+ }
+ }
+
+ getMatchStateLabel(state: MatchState): string {
+ switch (state) {
+ case MatchState.NOT_STARTED:
+ return 'À venir';
+ case MatchState.ONGOING:
+ return 'En cours';
+ case MatchState.OVER:
+ return 'Terminé';
+ default:
+ return 'Inconnu';
+ }
+ }
+
+ getMatchStateColor(state: MatchState): string {
+ switch (state) {
+ case MatchState.NOT_STARTED:
+ return 'secondary';
+ case MatchState.ONGOING:
+ return 'warning';
+ case MatchState.OVER:
+ return 'success';
+ default:
+ return 'light';
+ }
+ }
+
+ getRefereeLevelLabel(level: RefereeLevel): string {
+ switch (level) {
+ case RefereeLevel.NATIONAL:
+ return 'National';
+ case RefereeLevel.REGIONAL:
+ return 'Régional';
+ case RefereeLevel.DEPARTMENTAL:
+ return 'Départemental';
+ default:
+ return 'Inconnu';
+ }
+ }
+
+ updateScores(): void {
+ if (this.match) {
+ this.matchService.updateMatch(this.match.id, {
+ score1: this.match.score1,
+ score2: this.match.score2,
+ });
+ }
+ }
+
+ incrementScore(player: 1 | 2): void {
+ if (!this.match) return;
+
+ // Incrémente le score du joueur
+ if (player === 1) {
+ this.match.score1 += 1;
+ } else {
+ this.match.score2 += 1;
+ }
+
+ // Change l'état en fonction des scores
+ if (
+ this.match.score1 === 1 &&
+ this.match.score2 === 0 &&
+ this.match.state === MatchState.NOT_STARTED
+ ) {
+ this.match.state = MatchState.ONGOING; // passage à l'état 1 (en cours)
+ } else if (
+ this.match.score2 === 1 &&
+ this.match.score1 === 0 &&
+ this.match.state === MatchState.NOT_STARTED
+ ) {
+ this.match.state = MatchState.ONGOING;
+ }
+
+ // Si l'un des scores arrive à 15, on considère le match terminé
+ if (this.match.score1 >= 15 || this.match.score2 >= 15) {
+ this.match.state = MatchState.OVER; // passage à l'état 2 (terminé)
+ }
+
+ this.updateScores();
+ }
+}
diff --git a/FencerJudgeFront/src/app/components/matches/matches.component.html b/FencerJudgeFront/src/app/components/matches/matches.component.html
index a5561bb..e7036ad 100644
--- a/FencerJudgeFront/src/app/components/matches/matches.component.html
+++ b/FencerJudgeFront/src/app/components/matches/matches.component.html
@@ -6,6 +6,8 @@
diff --git a/FencerJudgeFront/src/app/components/matches/matches.component.ts b/FencerJudgeFront/src/app/components/matches/matches.component.ts
index 935e3d1..623f011 100644
--- a/FencerJudgeFront/src/app/components/matches/matches.component.ts
+++ b/FencerJudgeFront/src/app/components/matches/matches.component.ts
@@ -1,4 +1,5 @@
import { Component } from '@angular/core';
+import { Router } from '@angular/router';
import { MatchesService } from '@services/matches.service';
import { Matches, MatchState } from '@interfaces/matches';
@@ -19,7 +20,8 @@ export class MatchesComponent {
constructor(
private matchesService: MatchesService,
- private playerService: PlayerService
+ private playerService: PlayerService,
+ private router: Router
) {}
ngOnInit(): void {
@@ -63,4 +65,8 @@ export class MatchesComponent {
return 'Inconnu';
}
}
+
+ goToMatchDetail(matchId: number): void {
+ this.router.navigate(['/matches', matchId]);
+ }
}
diff --git a/FencerJudgeFront/src/app/services/matches.service.ts b/FencerJudgeFront/src/app/services/matches.service.ts
index 12a6201..fef45dd 100644
--- a/FencerJudgeFront/src/app/services/matches.service.ts
+++ b/FencerJudgeFront/src/app/services/matches.service.ts
@@ -68,4 +68,14 @@ export class MatchesService {
);
// ici tu pourrais plus tard faire : this.socket = new WebSocket('ws://...') etc.
}
+
+ updateMatch(id: number, updatedData: Partial
): void {
+ const match = this.matches.find((m) => m.id === id);
+ if (match) {
+ Object.assign(match, updatedData);
+ console.log(`[MatchesService] Match ${id} mis à jour :`, match);
+ } else {
+ console.warn(`[MatchesService] Match ${id} introuvable`);
+ }
+ }
}
--
2.45.3
From 5a8ea535979fc7bdbe8c91dcc815bce246337e22 Mon Sep 17 00:00:00 2001
From: ExostFlash
Date: Mon, 2 Jun 2025 16:17:17 +0200
Subject: [PATCH 06/17] modif
---
FencerJudgeFront/src/app/app-routing.module.ts | 8 ++++++--
FencerJudgeFront/src/app/app.module.ts | 4 ++--
.../app/components/essentials/header/header.component.ts | 2 +-
.../app/components/essentials/login/login.component.ts | 2 +-
.../app/components/essentials/logout/logout.component.ts | 2 +-
.../{ => match}/matches-id/matches-id.component.css | 0
.../{ => match}/matches-id/matches-id.component.html | 8 ++++----
.../{ => match}/matches-id/matches-id.component.spec.ts | 0
.../{ => match}/matches-id/matches-id.component.ts | 8 ++++----
.../components/{ => match}/matches/matches.component.css | 0
.../components/{ => match}/matches/matches.component.html | 0
.../{ => match}/matches/matches.component.spec.ts | 0
.../components/{ => match}/matches/matches.component.ts | 4 ++--
FencerJudgeFront/src/app/guards/auth.guard.ts | 2 +-
.../src/app/services/{ => auth}/auth.service.spec.ts | 0
.../src/app/services/{ => auth}/auth.service.ts | 0
.../app/services/{ => matches}/matches.service.spec.ts | 0
.../src/app/services/{ => matches}/matches.service.ts | 0
.../src/app/services/{ => player}/player.service.spec.ts | 0
.../src/app/services/{ => player}/player.service.ts | 0
.../app/services/{ => referee}/referee.service.spec.ts | 0
.../src/app/services/{ => referee}/referee.service.ts | 0
22 files changed, 22 insertions(+), 18 deletions(-)
rename FencerJudgeFront/src/app/components/{ => match}/matches-id/matches-id.component.css (100%)
rename FencerJudgeFront/src/app/components/{ => match}/matches-id/matches-id.component.html (90%)
rename FencerJudgeFront/src/app/components/{ => match}/matches-id/matches-id.component.spec.ts (100%)
rename FencerJudgeFront/src/app/components/{ => match}/matches-id/matches-id.component.ts (93%)
rename FencerJudgeFront/src/app/components/{ => match}/matches/matches.component.css (100%)
rename FencerJudgeFront/src/app/components/{ => match}/matches/matches.component.html (100%)
rename FencerJudgeFront/src/app/components/{ => match}/matches/matches.component.spec.ts (100%)
rename FencerJudgeFront/src/app/components/{ => match}/matches/matches.component.ts (93%)
rename FencerJudgeFront/src/app/services/{ => auth}/auth.service.spec.ts (100%)
rename FencerJudgeFront/src/app/services/{ => auth}/auth.service.ts (100%)
rename FencerJudgeFront/src/app/services/{ => matches}/matches.service.spec.ts (100%)
rename FencerJudgeFront/src/app/services/{ => matches}/matches.service.ts (100%)
rename FencerJudgeFront/src/app/services/{ => player}/player.service.spec.ts (100%)
rename FencerJudgeFront/src/app/services/{ => player}/player.service.ts (100%)
rename FencerJudgeFront/src/app/services/{ => referee}/referee.service.spec.ts (100%)
rename FencerJudgeFront/src/app/services/{ => referee}/referee.service.ts (100%)
diff --git a/FencerJudgeFront/src/app/app-routing.module.ts b/FencerJudgeFront/src/app/app-routing.module.ts
index fcdc89f..b958478 100644
--- a/FencerJudgeFront/src/app/app-routing.module.ts
+++ b/FencerJudgeFront/src/app/app-routing.module.ts
@@ -5,15 +5,19 @@ import { authGuard } from '@guards/auth.guard';
import { LoginComponent } from './components/essentials/login/login.component';
import { LogoutComponent } from './components/essentials/logout/logout.component';
+
import { HomeComponent } from './components/home/home.component';
-import { MatchesComponent } from './components/matches/matches.component';
-import { MatchesIdComponent } from './components/matches-id/matches-id.component';
+
+import { MatchesComponent } from './components/match/matches/matches.component';
+import { MatchesIdComponent } from './components/match/matches-id/matches-id.component';
const routes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: 'logout', component: LogoutComponent },
+
{ path: '', component: HomeComponent },
{ path: 'home', component: HomeComponent },
+
{ path: 'matches', component: MatchesComponent },
{ path: 'matches/:id', component: MatchesIdComponent },
];
diff --git a/FencerJudgeFront/src/app/app.module.ts b/FencerJudgeFront/src/app/app.module.ts
index 0c33646..e17ffe8 100644
--- a/FencerJudgeFront/src/app/app.module.ts
+++ b/FencerJudgeFront/src/app/app.module.ts
@@ -9,8 +9,8 @@ import { FooterComponent } from './components/essentials/footer/footer.component
import { HomeComponent } from './components/home/home.component';
import { LoginComponent } from './components/essentials/login/login.component';
import { LogoutComponent } from './components/essentials/logout/logout.component';
-import { MatchesComponent } from './components/matches/matches.component';
-import { MatchesIdComponent } from './components/matches-id/matches-id.component';
+import { MatchesComponent } from './components/match/matches/matches.component';
+import { MatchesIdComponent } from './components/match/matches-id/matches-id.component';
@NgModule({
declarations: [
diff --git a/FencerJudgeFront/src/app/components/essentials/header/header.component.ts b/FencerJudgeFront/src/app/components/essentials/header/header.component.ts
index 8d32d92..cb059ae 100644
--- a/FencerJudgeFront/src/app/components/essentials/header/header.component.ts
+++ b/FencerJudgeFront/src/app/components/essentials/header/header.component.ts
@@ -1,6 +1,6 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
-import { AuthService } from '@services/auth.service';
+import { AuthService } from '@services/auth/auth.service';
@Component({
selector: 'app-header',
diff --git a/FencerJudgeFront/src/app/components/essentials/login/login.component.ts b/FencerJudgeFront/src/app/components/essentials/login/login.component.ts
index 3ff2888..f7b137a 100644
--- a/FencerJudgeFront/src/app/components/essentials/login/login.component.ts
+++ b/FencerJudgeFront/src/app/components/essentials/login/login.component.ts
@@ -1,6 +1,6 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
-import { AuthService } from '@services/auth.service';
+import { AuthService } from '@services/auth/auth.service';
@Component({
selector: 'app-login',
diff --git a/FencerJudgeFront/src/app/components/essentials/logout/logout.component.ts b/FencerJudgeFront/src/app/components/essentials/logout/logout.component.ts
index 6da5b61..7945a95 100644
--- a/FencerJudgeFront/src/app/components/essentials/logout/logout.component.ts
+++ b/FencerJudgeFront/src/app/components/essentials/logout/logout.component.ts
@@ -1,6 +1,6 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
-import { AuthService } from '@services/auth.service';
+import { AuthService } from '@services/auth/auth.service';
@Component({
selector: 'app-logout',
diff --git a/FencerJudgeFront/src/app/components/matches-id/matches-id.component.css b/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.css
similarity index 100%
rename from FencerJudgeFront/src/app/components/matches-id/matches-id.component.css
rename to FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.css
diff --git a/FencerJudgeFront/src/app/components/matches-id/matches-id.component.html b/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.html
similarity index 90%
rename from FencerJudgeFront/src/app/components/matches-id/matches-id.component.html
rename to FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.html
index 38721f0..3e82262 100644
--- a/FencerJudgeFront/src/app/components/matches-id/matches-id.component.html
+++ b/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.html
@@ -21,9 +21,9 @@
- {{ player1?.firstName }} {{ player1?.name }}{{ player1.firstName }} {{ player1.name }}
- {{ player1?.club }}
+ {{ player1.club }}
@@ -58,9 +58,9 @@
- {{ player2?.firstName }} {{ player2?.name }}{{ player2.firstName }} {{ player2.name }}
- {{ player2?.club }}
+ {{ player2.club }}
diff --git a/FencerJudgeFront/src/app/components/matches-id/matches-id.component.spec.ts b/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.spec.ts
similarity index 100%
rename from FencerJudgeFront/src/app/components/matches-id/matches-id.component.spec.ts
rename to FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.spec.ts
diff --git a/FencerJudgeFront/src/app/components/matches-id/matches-id.component.ts b/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts
similarity index 93%
rename from FencerJudgeFront/src/app/components/matches-id/matches-id.component.ts
rename to FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts
index 2e0be56..65d932b 100644
--- a/FencerJudgeFront/src/app/components/matches-id/matches-id.component.ts
+++ b/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts
@@ -1,12 +1,12 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
-import { AuthService } from '@services/auth.service';
+import { AuthService } from '@services/auth/auth.service';
-import { MatchesService } from '@services/matches.service';
+import { MatchesService } from '@services/matches/matches.service';
import { MatchState, Matches } from '@interfaces/matches';
-import { PlayerService } from '@services/player.service';
+import { PlayerService } from '@services/player/player.service';
import { Player } from '@interfaces/player';
-import { RefereeService } from '@services/referee.service';
+import { RefereeService } from '@services/referee/referee.service';
import { Referee, RefereeLevel } from '@interfaces/referee';
@Component({
diff --git a/FencerJudgeFront/src/app/components/matches/matches.component.css b/FencerJudgeFront/src/app/components/match/matches/matches.component.css
similarity index 100%
rename from FencerJudgeFront/src/app/components/matches/matches.component.css
rename to FencerJudgeFront/src/app/components/match/matches/matches.component.css
diff --git a/FencerJudgeFront/src/app/components/matches/matches.component.html b/FencerJudgeFront/src/app/components/match/matches/matches.component.html
similarity index 100%
rename from FencerJudgeFront/src/app/components/matches/matches.component.html
rename to FencerJudgeFront/src/app/components/match/matches/matches.component.html
diff --git a/FencerJudgeFront/src/app/components/matches/matches.component.spec.ts b/FencerJudgeFront/src/app/components/match/matches/matches.component.spec.ts
similarity index 100%
rename from FencerJudgeFront/src/app/components/matches/matches.component.spec.ts
rename to FencerJudgeFront/src/app/components/match/matches/matches.component.spec.ts
diff --git a/FencerJudgeFront/src/app/components/matches/matches.component.ts b/FencerJudgeFront/src/app/components/match/matches/matches.component.ts
similarity index 93%
rename from FencerJudgeFront/src/app/components/matches/matches.component.ts
rename to FencerJudgeFront/src/app/components/match/matches/matches.component.ts
index 623f011..a1cf3dc 100644
--- a/FencerJudgeFront/src/app/components/matches/matches.component.ts
+++ b/FencerJudgeFront/src/app/components/match/matches/matches.component.ts
@@ -1,10 +1,10 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
-import { MatchesService } from '@services/matches.service';
+import { MatchesService } from '@services/matches/matches.service';
import { Matches, MatchState } from '@interfaces/matches';
-import { PlayerService } from '@services/player.service';
+import { PlayerService } from '@services/player/player.service';
import { Player } from '@interfaces/player';
@Component({
diff --git a/FencerJudgeFront/src/app/guards/auth.guard.ts b/FencerJudgeFront/src/app/guards/auth.guard.ts
index b3ed4e3..2da4546 100644
--- a/FencerJudgeFront/src/app/guards/auth.guard.ts
+++ b/FencerJudgeFront/src/app/guards/auth.guard.ts
@@ -1,6 +1,6 @@
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
-import { AuthService } from '@services/auth.service';
+import { AuthService } from '@services/auth/auth.service';
export const authGuard: CanActivateFn = (route, state) => {
const authService = inject(AuthService);
diff --git a/FencerJudgeFront/src/app/services/auth.service.spec.ts b/FencerJudgeFront/src/app/services/auth/auth.service.spec.ts
similarity index 100%
rename from FencerJudgeFront/src/app/services/auth.service.spec.ts
rename to FencerJudgeFront/src/app/services/auth/auth.service.spec.ts
diff --git a/FencerJudgeFront/src/app/services/auth.service.ts b/FencerJudgeFront/src/app/services/auth/auth.service.ts
similarity index 100%
rename from FencerJudgeFront/src/app/services/auth.service.ts
rename to FencerJudgeFront/src/app/services/auth/auth.service.ts
diff --git a/FencerJudgeFront/src/app/services/matches.service.spec.ts b/FencerJudgeFront/src/app/services/matches/matches.service.spec.ts
similarity index 100%
rename from FencerJudgeFront/src/app/services/matches.service.spec.ts
rename to FencerJudgeFront/src/app/services/matches/matches.service.spec.ts
diff --git a/FencerJudgeFront/src/app/services/matches.service.ts b/FencerJudgeFront/src/app/services/matches/matches.service.ts
similarity index 100%
rename from FencerJudgeFront/src/app/services/matches.service.ts
rename to FencerJudgeFront/src/app/services/matches/matches.service.ts
diff --git a/FencerJudgeFront/src/app/services/player.service.spec.ts b/FencerJudgeFront/src/app/services/player/player.service.spec.ts
similarity index 100%
rename from FencerJudgeFront/src/app/services/player.service.spec.ts
rename to FencerJudgeFront/src/app/services/player/player.service.spec.ts
diff --git a/FencerJudgeFront/src/app/services/player.service.ts b/FencerJudgeFront/src/app/services/player/player.service.ts
similarity index 100%
rename from FencerJudgeFront/src/app/services/player.service.ts
rename to FencerJudgeFront/src/app/services/player/player.service.ts
diff --git a/FencerJudgeFront/src/app/services/referee.service.spec.ts b/FencerJudgeFront/src/app/services/referee/referee.service.spec.ts
similarity index 100%
rename from FencerJudgeFront/src/app/services/referee.service.spec.ts
rename to FencerJudgeFront/src/app/services/referee/referee.service.spec.ts
diff --git a/FencerJudgeFront/src/app/services/referee.service.ts b/FencerJudgeFront/src/app/services/referee/referee.service.ts
similarity index 100%
rename from FencerJudgeFront/src/app/services/referee.service.ts
rename to FencerJudgeFront/src/app/services/referee/referee.service.ts
--
2.45.3
From d56d17724a99d8fa9341098bf14fc04b41a20fff Mon Sep 17 00:00:00 2001
From: DarkMax31
Date: Mon, 2 Jun 2025 16:18:54 +0200
Subject: [PATCH 07/17] front
---
.../essentials/footer/footer.component.css | 21 ++++
.../essentials/footer/footer.component.html | 3 +-
.../essentials/header/header.component.css | 25 +++++
.../essentials/header/header.component.html | 2 +-
.../essentials/login/login.component.css | 101 ++++++++++++++++++
.../essentials/login/login.component.html | 46 ++++----
.../app/components/home/home.component.html | 2 +-
7 files changed, 179 insertions(+), 21 deletions(-)
diff --git a/FencerJudgeFront/src/app/components/essentials/footer/footer.component.css b/FencerJudgeFront/src/app/components/essentials/footer/footer.component.css
index e69de29..a76370e 100644
--- a/FencerJudgeFront/src/app/components/essentials/footer/footer.component.css
+++ b/FencerJudgeFront/src/app/components/essentials/footer/footer.component.css
@@ -0,0 +1,21 @@
+
+.footer-gris {
+ background-color: #2c2c2c; /* Gris foncé personnalisé */
+}
+
+.flag-bar {
+ height: 6px; /* épaisseur du trait */
+ width: 100%; /* toute la largeur */
+ background: linear-gradient(
+ to right,
+ #0055a4 0%, /* bleu */
+ #0055a4 33.33%, /* 1/3 */
+ #fff 33.33%, /* blanc */
+ #fff 66.66%, /* 2/3 */
+ #ef4135 66.66%, /* rouge */
+ #ef4135 100%
+ );
+ margin-bottom: 8px;
+ border-radius: 3px; /* optionnel, pour arrondir les bords */
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+}
\ No newline at end of file
diff --git a/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html b/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html
index d280498..01eca56 100644
--- a/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html
+++ b/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html
@@ -1,4 +1,4 @@
-
\ No newline at end of file
--
2.45.3
From 5a73a70e0252b22c51b2a3a78e858d4ba25f9664 Mon Sep 17 00:00:00 2001
From: ExostFlash
Date: Mon, 2 Jun 2025 18:35:15 +0200
Subject: [PATCH 10/17] add Matches Add
---
.../src/app/app-routing.module.ts | 6 ++
FencerJudgeFront/src/app/app.module.ts | 2 +
.../essentials/footer/footer.component.html | 7 +-
.../essentials/header/header.component.html | 9 ++
.../matches-add/matches-add.component.css | 0
.../matches-add/matches-add.component.html | 1 +
.../matches-add/matches-add.component.spec.ts | 23 +++++
.../matches-add/matches-add.component.ts | 96 +++++++++++++++++++
.../match/matches-id/matches-id.component.ts | 2 +
.../app/services/matches/matches.service.ts | 12 +++
.../src/app/services/player/player.service.ts | 31 ++++++
.../app/services/referee/referee.service.ts | 31 ++++++
db.lock.db | 6 ++
13 files changed, 220 insertions(+), 6 deletions(-)
create mode 100644 FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.css
create mode 100644 FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.html
create mode 100644 FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.spec.ts
create mode 100644 FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.ts
create mode 100644 db.lock.db
diff --git a/FencerJudgeFront/src/app/app-routing.module.ts b/FencerJudgeFront/src/app/app-routing.module.ts
index b958478..a1cc001 100644
--- a/FencerJudgeFront/src/app/app-routing.module.ts
+++ b/FencerJudgeFront/src/app/app-routing.module.ts
@@ -10,6 +10,7 @@ import { HomeComponent } from './components/home/home.component';
import { MatchesComponent } from './components/match/matches/matches.component';
import { MatchesIdComponent } from './components/match/matches-id/matches-id.component';
+import { MatchesAddComponent } from './components/match/matches-add/matches-add.component';
const routes: Routes = [
{ path: 'login', component: LoginComponent },
@@ -19,6 +20,11 @@ const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: 'matches', component: MatchesComponent },
+ {
+ path: 'matches/add',
+ component: MatchesAddComponent,
+ canActivate: [authGuard],
+ },
{ path: 'matches/:id', component: MatchesIdComponent },
];
diff --git a/FencerJudgeFront/src/app/app.module.ts b/FencerJudgeFront/src/app/app.module.ts
index e17ffe8..f035f52 100644
--- a/FencerJudgeFront/src/app/app.module.ts
+++ b/FencerJudgeFront/src/app/app.module.ts
@@ -11,6 +11,7 @@ import { LoginComponent } from './components/essentials/login/login.component';
import { LogoutComponent } from './components/essentials/logout/logout.component';
import { MatchesComponent } from './components/match/matches/matches.component';
import { MatchesIdComponent } from './components/match/matches-id/matches-id.component';
+import { MatchesAddComponent } from './components/match/matches-add/matches-add.component';
@NgModule({
declarations: [
@@ -22,6 +23,7 @@ import { MatchesIdComponent } from './components/match/matches-id/matches-id.com
LogoutComponent,
MatchesComponent,
MatchesIdComponent,
+ MatchesAddComponent,
],
imports: [BrowserModule, AppRoutingModule, FormsModule],
providers: [],
diff --git a/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html b/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html
index 01eca56..86a3a7b 100644
--- a/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html
+++ b/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html
@@ -47,12 +47,7 @@
-
© FencerJudge - Tous droits réservés
-
Mentions légales
-
Cookies
-
Accessibilité
-
Nous contacter
-
Presse
+
© FencerJudge - Tous droits réservés
diff --git a/FencerJudgeFront/src/app/components/essentials/header/header.component.html b/FencerJudgeFront/src/app/components/essentials/header/header.component.html
index d0e7a6f..c8ae8e8 100644
--- a/FencerJudgeFront/src/app/components/essentials/header/header.component.html
+++ b/FencerJudgeFront/src/app/components/essentials/header/header.component.html
@@ -21,6 +21,15 @@
Matchs
+
+
+ MatchAdd
+
+
matches-add works!
diff --git a/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.spec.ts b/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.spec.ts
new file mode 100644
index 0000000..5cd8289
--- /dev/null
+++ b/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MatchesAddComponent } from './matches-add.component';
+
+describe('MatchesAddComponent', () => {
+ let component: MatchesAddComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [MatchesAddComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(MatchesAddComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.ts b/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.ts
new file mode 100644
index 0000000..1de2680
--- /dev/null
+++ b/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.ts
@@ -0,0 +1,96 @@
+import { Component } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { MatchesService } from '@services/matches/matches.service';
+import { MatchState } from '@interfaces/matches';
+
+import { PlayerService } from '@services/player/player.service';
+
+import { RefereeService } from '@services/referee/referee.service';
+import { Referee, RefereeLevel } from '@interfaces/referee';
+
+@Component({
+ selector: 'app-matches-add',
+ standalone: false,
+ templateUrl: './matches-add.component.html',
+ styleUrl: './matches-add.component.css',
+})
+export class MatchesAddComponent {
+ formData = {
+ player1Id: 0,
+ player1FirstName: '',
+ player1Name: '',
+ player1Club: '',
+
+ player2Id: 0,
+ player2FirstName: '',
+ player2Name: '',
+ player2Club: '',
+
+ refereeId: 0,
+ refereeFirstName: '',
+ refereeName: '',
+ refereelevel: 0,
+
+ city: '',
+ country: '',
+ date: '',
+ weapon: '',
+ };
+
+ constructor(
+ private matchService: MatchesService,
+ private playerService: PlayerService,
+ private refereeService: RefereeService,
+ private router: Router
+ ) {}
+
+ onSubmit(): void {
+ this.playerService
+ .getOrCreateByName(
+ this.formData.player1FirstName,
+ this.formData.player1Name,
+ this.formData.player1Club
+ )
+ .subscribe((player1) => {
+ this.formData['player1Id'] = player1.id;
+
+ this.playerService
+ .getOrCreateByName(
+ this.formData.player2FirstName,
+ this.formData.player2Name,
+ this.formData.player2Club
+ )
+ .subscribe((player2) => {
+ this.formData['player2Id'] = player2.id;
+
+ this.refereeService
+ .getOrCreateByName(
+ this.formData.refereeFirstName,
+ this.formData.refereeName,
+ this.formData.refereelevel
+ )
+ .subscribe((referee) => {
+ this.formData['refereeId'] = referee.id;
+
+ const matchPayload = {
+ refereeID: referee.id,
+ player1ID: player1.id,
+ score1: 0,
+ player2ID: player2.id,
+ score2: 0,
+ country: this.formData.country,
+ city: this.formData.city,
+ weapon: this.formData.weapon,
+ date: new Date(this.formData.date),
+ state: MatchState.NOT_STARTED,
+ };
+
+ this.matchService.create(matchPayload).subscribe(() => {
+ this.router.navigate(['/matches']);
+ });
+ });
+ });
+ });
+ }
+}
diff --git a/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts b/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts
index 65d932b..7dbce17 100644
--- a/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts
+++ b/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts
@@ -4,8 +4,10 @@ import { AuthService } from '@services/auth/auth.service';
import { MatchesService } from '@services/matches/matches.service';
import { MatchState, Matches } from '@interfaces/matches';
+
import { PlayerService } from '@services/player/player.service';
import { Player } from '@interfaces/player';
+
import { RefereeService } from '@services/referee/referee.service';
import { Referee, RefereeLevel } from '@interfaces/referee';
diff --git a/FencerJudgeFront/src/app/services/matches/matches.service.ts b/FencerJudgeFront/src/app/services/matches/matches.service.ts
index fef45dd..58e004c 100644
--- a/FencerJudgeFront/src/app/services/matches/matches.service.ts
+++ b/FencerJudgeFront/src/app/services/matches/matches.service.ts
@@ -45,6 +45,7 @@ export class MatchesService {
state: MatchState.NOT_STARTED,
},
];
+ private nextId = 1;
constructor() {
console.log('[MatchesService] Initial matches loaded:', this.matches);
@@ -78,4 +79,15 @@ export class MatchesService {
console.warn(`[MatchesService] Match ${id} introuvable`);
}
}
+
+ create(match: Omit): Observable {
+ const newMatch: Matches = {
+ ...match,
+ id: this.nextId++,
+ };
+
+ this.matches.push(newMatch);
+ console.log('[MatchesService] Match created:', newMatch);
+ return of(newMatch);
+ }
}
diff --git a/FencerJudgeFront/src/app/services/player/player.service.ts b/FencerJudgeFront/src/app/services/player/player.service.ts
index 53d4350..c5f3a1b 100644
--- a/FencerJudgeFront/src/app/services/player/player.service.ts
+++ b/FencerJudgeFront/src/app/services/player/player.service.ts
@@ -27,4 +27,35 @@ export class PlayerService {
console.log(`[PlayerService] Fetching player ID: ${id}`, player);
return of(player);
}
+
+ getOrCreateByName(
+ firstName: string,
+ name: string,
+ club: string = 'Inconnu'
+ ): Observable {
+ const existing = this.players.find(
+ (p) =>
+ p.firstName.toLowerCase() === firstName.toLowerCase() &&
+ p.name.toLowerCase() === name.toLowerCase()
+ );
+
+ if (existing) {
+ console.log('[PlayerService] Joueur trouvé :', existing);
+ return of(existing);
+ }
+
+ const newPlayer: Player = {
+ id:
+ this.players.length > 0
+ ? Math.max(...this.players.map((p) => p.id)) + 1
+ : 1,
+ firstName,
+ name,
+ club,
+ };
+
+ this.players.push(newPlayer);
+ console.log('[PlayerService] Nouveau joueur créé :', newPlayer);
+ return of(newPlayer);
+ }
}
diff --git a/FencerJudgeFront/src/app/services/referee/referee.service.ts b/FencerJudgeFront/src/app/services/referee/referee.service.ts
index 6963230..8d26a06 100644
--- a/FencerJudgeFront/src/app/services/referee/referee.service.ts
+++ b/FencerJudgeFront/src/app/services/referee/referee.service.ts
@@ -39,4 +39,35 @@ export class RefereeService {
console.log(`[RefereeService] Fetching referee ID: ${id}`, ref);
return of(ref);
}
+
+ getOrCreateByName(
+ firstName: string,
+ name: string,
+ level: RefereeLevel
+ ): Observable {
+ const existing = this.referees.find(
+ (r) =>
+ r.firstName.toLowerCase() === firstName.toLowerCase() &&
+ r.name.toLowerCase() === name.toLowerCase()
+ );
+
+ if (existing) {
+ console.log('[RefereeService] Arbitre trouvé :', existing);
+ return of(existing);
+ }
+
+ const newPlayer: Referee = {
+ id:
+ this.referees.length > 0
+ ? Math.max(...this.referees.map((r) => r.id)) + 1
+ : 1,
+ firstName,
+ name,
+ level,
+ };
+
+ this.referees.push(newPlayer);
+ console.log('[RefereeService] Nouveau arbitre créé :', newPlayer);
+ return of(newPlayer);
+ }
}
diff --git a/db.lock.db b/db.lock.db
new file mode 100644
index 0000000..072e750
--- /dev/null
+++ b/db.lock.db
@@ -0,0 +1,6 @@
+#FileLock
+#Mon Jun 02 17:40:34 CEST 2025
+hostName=ExostFlash
+id=197313393c88a931fa21bf5ce1281b7253870b00683
+method=file
+server=192.167.2.100\:54366
--
2.45.3
From 7782d6f50ab99f1a083088fa14a025c89d201a4e Mon Sep 17 00:00:00 2001
From: ExostFlash
Date: Mon, 2 Jun 2025 19:00:03 +0200
Subject: [PATCH 11/17] Add matchesadd
---
.../essentials/login/login.component.html | 20 +++-
.../matches-add/matches-add.component.html | 103 +++++++++++++++++-
.../matches-add/matches-add.component.ts | 4 +-
.../app/services/matches/matches.service.ts | 4 +-
4 files changed, 123 insertions(+), 8 deletions(-)
diff --git a/FencerJudgeFront/src/app/components/essentials/login/login.component.html b/FencerJudgeFront/src/app/components/essentials/login/login.component.html
index c531f0b..2eca917 100644
--- a/FencerJudgeFront/src/app/components/essentials/login/login.component.html
+++ b/FencerJudgeFront/src/app/components/essentials/login/login.component.html
@@ -5,16 +5,30 @@
-
\ No newline at end of file
+
diff --git a/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.html b/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.html
index ad70c67..0e64f71 100644
--- a/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.html
+++ b/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.html
@@ -1 +1,102 @@
-matches-add works!
+Ajouter un match
+
+
diff --git a/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.ts b/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.ts
index 1de2680..b0acd38 100644
--- a/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.ts
+++ b/FencerJudgeFront/src/app/components/match/matches-add/matches-add.component.ts
@@ -1,5 +1,6 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
+import { FormsModule } from '@angular/forms';
import { MatchesService } from '@services/matches/matches.service';
import { MatchState } from '@interfaces/matches';
@@ -34,7 +35,6 @@ export class MatchesAddComponent {
city: '',
country: '',
- date: '',
weapon: '',
};
@@ -82,7 +82,7 @@ export class MatchesAddComponent {
country: this.formData.country,
city: this.formData.city,
weapon: this.formData.weapon,
- date: new Date(this.formData.date),
+ date: new Date(),
state: MatchState.NOT_STARTED,
};
diff --git a/FencerJudgeFront/src/app/services/matches/matches.service.ts b/FencerJudgeFront/src/app/services/matches/matches.service.ts
index 58e004c..9e00fca 100644
--- a/FencerJudgeFront/src/app/services/matches/matches.service.ts
+++ b/FencerJudgeFront/src/app/services/matches/matches.service.ts
@@ -45,7 +45,7 @@ export class MatchesService {
state: MatchState.NOT_STARTED,
},
];
- private nextId = 1;
+ private nextMatchId = Math.max(...this.matches.map((m) => m.id)) + 1;
constructor() {
console.log('[MatchesService] Initial matches loaded:', this.matches);
@@ -83,7 +83,7 @@ export class MatchesService {
create(match: Omit): Observable {
const newMatch: Matches = {
...match,
- id: this.nextId++,
+ id: this.nextMatchId++,
};
this.matches.push(newMatch);
--
2.45.3
From 79413081bdba08f5f1452aadb5332b10c3924d08 Mon Sep 17 00:00:00 2001
From: ExostFlash
Date: Mon, 2 Jun 2025 21:19:27 +0200
Subject: [PATCH 12/17] Add sponsort
---
FencerJudgeFront/src/app/app.module.ts | 2 +
.../app/components/home/home.component.html | 2 +-
.../templates/sponsort/sponsort.component.css | 56 +++++++++++++++++++
.../sponsort/sponsort.component.html | 23 ++++++++
.../sponsort/sponsort.component.spec.ts | 23 ++++++++
.../templates/sponsort/sponsort.component.ts | 11 ++++
6 files changed, 116 insertions(+), 1 deletion(-)
create mode 100644 FencerJudgeFront/src/app/templates/sponsort/sponsort.component.css
create mode 100644 FencerJudgeFront/src/app/templates/sponsort/sponsort.component.html
create mode 100644 FencerJudgeFront/src/app/templates/sponsort/sponsort.component.spec.ts
create mode 100644 FencerJudgeFront/src/app/templates/sponsort/sponsort.component.ts
diff --git a/FencerJudgeFront/src/app/app.module.ts b/FencerJudgeFront/src/app/app.module.ts
index f035f52..e3032f2 100644
--- a/FencerJudgeFront/src/app/app.module.ts
+++ b/FencerJudgeFront/src/app/app.module.ts
@@ -12,6 +12,7 @@ import { LogoutComponent } from './components/essentials/logout/logout.component
import { MatchesComponent } from './components/match/matches/matches.component';
import { MatchesIdComponent } from './components/match/matches-id/matches-id.component';
import { MatchesAddComponent } from './components/match/matches-add/matches-add.component';
+import { SponsortComponent } from './templates/sponsort/sponsort.component';
@NgModule({
declarations: [
@@ -24,6 +25,7 @@ import { MatchesAddComponent } from './components/match/matches-add/matches-add.
MatchesComponent,
MatchesIdComponent,
MatchesAddComponent,
+ SponsortComponent,
],
imports: [BrowserModule, AppRoutingModule, FormsModule],
providers: [],
diff --git a/FencerJudgeFront/src/app/components/home/home.component.html b/FencerJudgeFront/src/app/components/home/home.component.html
index a1ac75f..f761df7 100644
--- a/FencerJudgeFront/src/app/components/home/home.component.html
+++ b/FencerJudgeFront/src/app/components/home/home.component.html
@@ -1 +1 @@
-HELLO !
+
diff --git a/FencerJudgeFront/src/app/templates/sponsort/sponsort.component.css b/FencerJudgeFront/src/app/templates/sponsort/sponsort.component.css
new file mode 100644
index 0000000..13c800a
--- /dev/null
+++ b/FencerJudgeFront/src/app/templates/sponsort/sponsort.component.css
@@ -0,0 +1,56 @@
+@keyframes slides {
+ from {
+ transform: translateX(0);
+ }
+ to {
+ transform: translateX(-100%);
+ }
+}
+
+.logos {
+ overflow: hidden;
+ padding: 30px 0px;
+ white-space: nowrap;
+ position: relative;
+}
+
+.logos:before,
+.logos:after {
+ position: absolute;
+ top: 0;
+ content: "";
+ width: 250px;
+ height: 100%;
+ z-index: 2;
+}
+
+.logos:before {
+ left: 0;
+ background: linear-gradient(
+ to left,
+ rgba(255, 255, 255, 0),
+ rgb(255, 255, 255)
+ );
+}
+
+.logos:after {
+ right: 0;
+ background: linear-gradient(
+ to right,
+ rgba(255, 255, 255, 0),
+ rgb(255, 255, 255)
+ );
+}
+
+.logo_items {
+ display: inline-block;
+ animation: 35s slides infinite linear;
+}
+
+.logos:hover .logo_items {
+ animation-play-state: paused;
+}
+
+.logo_items img {
+ height: 100px;
+}
diff --git a/FencerJudgeFront/src/app/templates/sponsort/sponsort.component.html b/FencerJudgeFront/src/app/templates/sponsort/sponsort.component.html
new file mode 100644
index 0000000..89ce529
--- /dev/null
+++ b/FencerJudgeFront/src/app/templates/sponsort/sponsort.component.html
@@ -0,0 +1,23 @@
+
diff --git a/FencerJudgeFront/src/app/templates/sponsort/sponsort.component.spec.ts b/FencerJudgeFront/src/app/templates/sponsort/sponsort.component.spec.ts
new file mode 100644
index 0000000..9ca3691
--- /dev/null
+++ b/FencerJudgeFront/src/app/templates/sponsort/sponsort.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SponsortComponent } from './sponsort.component';
+
+describe('SponsortComponent', () => {
+ let component: SponsortComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [SponsortComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(SponsortComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/FencerJudgeFront/src/app/templates/sponsort/sponsort.component.ts b/FencerJudgeFront/src/app/templates/sponsort/sponsort.component.ts
new file mode 100644
index 0000000..8058d61
--- /dev/null
+++ b/FencerJudgeFront/src/app/templates/sponsort/sponsort.component.ts
@@ -0,0 +1,11 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-sponsort',
+ standalone: false,
+ templateUrl: './sponsort.component.html',
+ styleUrl: './sponsort.component.css'
+})
+export class SponsortComponent {
+
+}
--
2.45.3
From 09dbb30611db96e371a0e34ce5376324d396e9f5 Mon Sep 17 00:00:00 2001
From: ExostFlash
Date: Mon, 2 Jun 2025 22:18:29 +0200
Subject: [PATCH 13/17] Add match-del & button del, fix services
---
.../src/app/app-routing.module.ts | 6 +++
FencerJudgeFront/src/app/app.module.ts | 2 +
.../essentials/footer/footer.component.html | 14 ++-----
.../matches-del/matches-del.component.css | 0
.../matches-del/matches-del.component.html | 1 +
.../matches-del/matches-del.component.spec.ts | 23 +++++++++++
.../matches-del/matches-del.component.ts | 38 +++++++++++++++++++
.../matches-id/matches-id.component.html | 2 +
.../match/matches-id/matches-id.component.ts | 12 +++++-
.../app/services/matches/matches.service.ts | 14 +++++++
10 files changed, 100 insertions(+), 12 deletions(-)
create mode 100644 FencerJudgeFront/src/app/components/match/matches-del/matches-del.component.css
create mode 100644 FencerJudgeFront/src/app/components/match/matches-del/matches-del.component.html
create mode 100644 FencerJudgeFront/src/app/components/match/matches-del/matches-del.component.spec.ts
create mode 100644 FencerJudgeFront/src/app/components/match/matches-del/matches-del.component.ts
diff --git a/FencerJudgeFront/src/app/app-routing.module.ts b/FencerJudgeFront/src/app/app-routing.module.ts
index a1cc001..0440f68 100644
--- a/FencerJudgeFront/src/app/app-routing.module.ts
+++ b/FencerJudgeFront/src/app/app-routing.module.ts
@@ -11,6 +11,7 @@ import { HomeComponent } from './components/home/home.component';
import { MatchesComponent } from './components/match/matches/matches.component';
import { MatchesIdComponent } from './components/match/matches-id/matches-id.component';
import { MatchesAddComponent } from './components/match/matches-add/matches-add.component';
+import { MatchesDelComponent } from './components/match/matches-del/matches-del.component';
const routes: Routes = [
{ path: 'login', component: LoginComponent },
@@ -25,6 +26,11 @@ const routes: Routes = [
component: MatchesAddComponent,
canActivate: [authGuard],
},
+ {
+ path: 'matches/del/:id',
+ component: MatchesDelComponent,
+ canActivate: [authGuard],
+ },
{ path: 'matches/:id', component: MatchesIdComponent },
];
diff --git a/FencerJudgeFront/src/app/app.module.ts b/FencerJudgeFront/src/app/app.module.ts
index e3032f2..4490d31 100644
--- a/FencerJudgeFront/src/app/app.module.ts
+++ b/FencerJudgeFront/src/app/app.module.ts
@@ -13,6 +13,7 @@ import { MatchesComponent } from './components/match/matches/matches.component';
import { MatchesIdComponent } from './components/match/matches-id/matches-id.component';
import { MatchesAddComponent } from './components/match/matches-add/matches-add.component';
import { SponsortComponent } from './templates/sponsort/sponsort.component';
+import { MatchesDelComponent } from './components/match/matches-del/matches-del.component';
@NgModule({
declarations: [
@@ -26,6 +27,7 @@ import { SponsortComponent } from './templates/sponsort/sponsort.component';
MatchesIdComponent,
MatchesAddComponent,
SponsortComponent,
+ MatchesDelComponent,
],
imports: [BrowserModule, AppRoutingModule, FormsModule],
providers: [],
diff --git a/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html b/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html
index 86a3a7b..a110ff8 100644
--- a/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html
+++ b/FencerJudgeFront/src/app/components/essentials/footer/footer.component.html
@@ -15,31 +15,25 @@
>
Suivez-nous
-
matches-del works!
diff --git a/FencerJudgeFront/src/app/components/match/matches-del/matches-del.component.spec.ts b/FencerJudgeFront/src/app/components/match/matches-del/matches-del.component.spec.ts
new file mode 100644
index 0000000..29459b8
--- /dev/null
+++ b/FencerJudgeFront/src/app/components/match/matches-del/matches-del.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MatchesDelComponent } from './matches-del.component';
+
+describe('MatchesDelComponent', () => {
+ let component: MatchesDelComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [MatchesDelComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(MatchesDelComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/FencerJudgeFront/src/app/components/match/matches-del/matches-del.component.ts b/FencerJudgeFront/src/app/components/match/matches-del/matches-del.component.ts
new file mode 100644
index 0000000..b3b9bd4
--- /dev/null
+++ b/FencerJudgeFront/src/app/components/match/matches-del/matches-del.component.ts
@@ -0,0 +1,38 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+
+import { AuthService } from '@services/auth/auth.service';
+import { MatchesService } from '@services/matches/matches.service';
+
+@Component({
+ selector: 'app-matches-del',
+ standalone: false,
+ templateUrl: './matches-del.component.html',
+ styleUrl: './matches-del.component.css',
+})
+export class MatchesDelComponent {
+ constructor(
+ private route: ActivatedRoute,
+ public authService: AuthService,
+ private router: Router,
+ private matchService: MatchesService
+ ) {}
+
+ ngOnInit(): void {
+ const id = Number(this.route.snapshot.paramMap.get('id'));
+
+ if (!isNaN(id)) {
+ this.matchService.deleteMatch(id).subscribe({
+ next: () => {
+ console.log(`Match ${id} supprimé`);
+ this.router.navigate(['/matches']); // redirection après suppression
+ },
+ error: (err) => {
+ console.error('Erreur lors de la suppression :', err);
+ },
+ });
+ } else {
+ console.error('ID de match invalide');
+ }
+ }
+}
diff --git a/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.html b/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.html
index 3e82262..2f926a0 100644
--- a/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.html
+++ b/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.html
@@ -92,3 +92,5 @@
+
+Delete
diff --git a/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts b/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts
index 7dbce17..7430beb 100644
--- a/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts
+++ b/FencerJudgeFront/src/app/components/match/matches-id/matches-id.component.ts
@@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
-import { ActivatedRoute } from '@angular/router';
+import { Router, ActivatedRoute } from '@angular/router';
import { AuthService } from '@services/auth/auth.service';
import { MatchesService } from '@services/matches/matches.service';
@@ -28,7 +28,8 @@ export class MatchesIdComponent implements OnInit {
private matchService: MatchesService,
private playerService: PlayerService,
private refereeService: RefereeService,
- public authService: AuthService
+ public authService: AuthService,
+ private router: Router
) {}
ngOnInit(): void {
@@ -140,4 +141,11 @@ export class MatchesIdComponent implements OnInit {
this.updateScores();
}
+
+ goToMatchDel(): void {
+ this.router.navigate([
+ '/matches/del/',
+ this.route.snapshot.paramMap.get('id'),
+ ]);
+ }
}
diff --git a/FencerJudgeFront/src/app/services/matches/matches.service.ts b/FencerJudgeFront/src/app/services/matches/matches.service.ts
index 9e00fca..bbffd36 100644
--- a/FencerJudgeFront/src/app/services/matches/matches.service.ts
+++ b/FencerJudgeFront/src/app/services/matches/matches.service.ts
@@ -90,4 +90,18 @@ export class MatchesService {
console.log('[MatchesService] Match created:', newMatch);
return of(newMatch);
}
+
+ deleteMatch(id: number): Observable {
+ const index = this.matches.findIndex((m) => m.id === id);
+
+ if (index !== -1) {
+ const deletedMatch = this.matches.splice(index, 1)[0];
+ console.log(`[MatchesService] Match ${id} supprimé :`, deletedMatch);
+
+ return of(true);
+ } else {
+ console.warn(`[MatchesService] Match ${id} introuvable pour suppression`);
+ return of(false);
+ }
+ }
}
--
2.45.3
From aa6228aec0a581f97bdf31e6d3107cf7d9ea08f0 Mon Sep 17 00:00:00 2001
From: kelen-dev
Date: Mon, 2 Jun 2025 22:30:38 +0200
Subject: [PATCH 14/17] add match card
---
FencerJudgeFront/src/app/app.component.html | 4 +-
.../essentials/login/login.component.css | 1 +
.../essentials/login/login.component.html | 22 +-
.../app/components/home/home.component.css | 560 ++++++++++++++++++
.../app/components/home/home.component.html | 92 ++-
FencerJudgeFront/src/assets/duel.webp | Bin 0 -> 174832 bytes
6 files changed, 658 insertions(+), 21 deletions(-)
create mode 100644 FencerJudgeFront/src/assets/duel.webp
diff --git a/FencerJudgeFront/src/app/app.component.html b/FencerJudgeFront/src/app/app.component.html
index a37d5f5..eaf43d3 100644
--- a/FencerJudgeFront/src/app/app.component.html
+++ b/FencerJudgeFront/src/app/app.component.html
@@ -1,5 +1,3 @@
-
-
-
+
\ No newline at end of file
diff --git a/FencerJudgeFront/src/app/components/essentials/login/login.component.css b/FencerJudgeFront/src/app/components/essentials/login/login.component.css
index 6093560..a2b0cf2 100644
--- a/FencerJudgeFront/src/app/components/essentials/login/login.component.css
+++ b/FencerJudgeFront/src/app/components/essentials/login/login.component.css
@@ -41,6 +41,7 @@
inset: 0;
background: rgba(0, 0, 0, 0.3);
/* voile sombre */
+ -webkit-backdrop-filter: blur(2px);
backdrop-filter: blur(2px);
/* effet de flou */
z-index: 1;
diff --git a/FencerJudgeFront/src/app/components/essentials/login/login.component.html b/FencerJudgeFront/src/app/components/essentials/login/login.component.html
index 2eca917..993b80b 100644
--- a/FencerJudgeFront/src/app/components/essentials/login/login.component.html
+++ b/FencerJudgeFront/src/app/components/essentials/login/login.component.html
@@ -5,30 +5,18 @@
-
+
\ No newline at end of file
diff --git a/FencerJudgeFront/src/app/components/home/home.component.css b/FencerJudgeFront/src/app/components/home/home.component.css
index e69de29..4e85ad9 100644
--- a/FencerJudgeFront/src/app/components/home/home.component.css
+++ b/FencerJudgeFront/src/app/components/home/home.component.css
@@ -0,0 +1,560 @@
+.match-card {
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
+ border: 2px solid #daa520;
+ border-radius: 16px;
+ padding: 24px;
+ box-shadow:
+ 0 8px 32px rgba(0, 0, 0, 0.4),
+ inset 0 1px 0 rgba(255, 255, 255, 0.1);
+ position: relative;
+ overflow: hidden;
+ transition: all 0.3s ease;
+ height: 80vh;
+ width: 100%;
+}
+
+.match-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 2px;
+ background: linear-gradient(90deg, #daa520, #ffd700, #daa520);
+ background-size: 200% 100%;
+ animation: gradientShift 15s ease infinite;
+}
+
+@keyframes gradientShift {
+ 0% {
+ background-position: 0% 50%;
+ }
+
+ 50% {
+ background-position: 100% 50%;
+ }
+
+ 100% {
+ background-position: 0% 50%;
+ }
+}
+
+.match-card:hover {
+ transform: translateY(-4px);
+ box-shadow:
+ 0 12px 48px rgba(218, 165, 32, 0.2),
+ 0 8px 32px rgba(0, 0, 0, 0.4);
+}
+
+.match-background {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: -1;
+}
+
+.background-image {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ filter: grayscale(30%) brightness(0.4) contrast(1.2);
+ transition: all 0.5s ease;
+}
+
+.match-card:hover .background-image {
+ filter: grayscale(10%) brightness(0.3) contrast(1.4);
+ transform: scale(1.05);
+}
+
+.image-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background:
+ linear-gradient(135deg,
+ rgba(26, 26, 46, 0.85) 0%,
+ rgba(0, 0, 0, 0.7) 50%,
+ rgba(22, 33, 62, 0.85) 100%
+ );
+}
+
+/* Header */
+.match-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+}
+
+.match-weapon {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ color: #daa520;
+ font-weight: 600;
+}
+
+.weapon-type {
+ font-size: 14px;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+}
+
+/* Status */
+.match-status {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 6px 12px;
+ border-radius: 20px;
+ font-size: 12px;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.status-live {
+ background: rgba(34, 197, 94, 0.2);
+ color: #22c55e;
+ border: 1px solid #22c55e;
+}
+
+.status-finished {
+ background: rgba(239, 68, 68, 0.2);
+ color: #ef4444;
+ border: 1px solid #ef4444;
+}
+
+.status-upcoming {
+ background: rgba(59, 130, 246, 0.2);
+ color: #3b82f6;
+ border: 1px solid #3b82f6;
+}
+
+.status-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: currentColor;
+ animation: pulse 2s infinite;
+}
+
+@keyframes pulse {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.5; }
+}
+
+/* Versus Section */
+.match-versus {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin: 24px 0;
+ position: relative;
+}
+
+.fencer {
+ flex: 1;
+ text-align: center;
+ color: white;
+}
+
+.fencer-name {
+ font-size: 18px;
+ font-weight: 700;
+ margin-bottom: 4px;
+ color: #daa520;
+}
+
+.fencer-club {
+ font-size: 12px;
+ color: #9ca3af;
+ margin-bottom: 8px;
+}
+
+.score {
+ font-size: 32px;
+ font-weight: 900;
+ color: white;
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
+}
+
+.vs-divider {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+ margin: 0 20px;
+}
+
+.vs-text {
+ font-size: 14px;
+ font-weight: 700;
+ color: #6b7280;
+ letter-spacing: 2px;
+}
+
+.crossed-swords {
+ font-size: 20px;
+ opacity: 0.7;
+}
+
+/* Details */
+.match-details {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ margin-top: 20px;
+ padding-top: 20px;
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.detail-item {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ font-size: 14px;
+}
+
+.detail-item .icon {
+ font-size: 16px;
+ width: 20px;
+ text-align: center;
+}
+
+.detail-item .label {
+ color: #9ca3af;
+ font-weight: 500;
+ min-width: 60px;
+}
+
+.detail-item .value {
+ color: white;
+ font-weight: 600;
+}
+
+/* Responsive */
+@media (max-width: 768px) {
+ .match-card {
+ padding: 16px;
+ }
+
+ .match-versus {
+ flex-direction: column;
+ gap: 16px;
+ }
+
+ .vs-divider {
+ transform: rotate(90deg);
+ margin: 0;
+ }
+
+ .fencer {
+ width: 100%;
+ }
+}
+
+.combat-arena {
+ margin-top: 24px;
+ background:
+ linear-gradient(135deg,
+ rgba(220, 38, 38, 0.1) 0%,
+ rgba(0, 0, 0, 0.8) 50%,
+ rgba(255, 215, 0, 0.1) 100%
+ );
+ border: 2px solid;
+ border-image: linear-gradient(45deg, #dc2626, #ffd700, #dc2626) 1;
+ border-radius: 12px;
+ padding: 20px;
+ position: relative;
+ overflow: hidden;
+}
+
+.combat-arena::before {
+ content: '';
+ position: absolute;
+ top: -50%;
+ left: -50%;
+ width: 200%;
+ height: 200%;
+ background:
+ repeating-conic-gradient(
+ from 0deg at 50% 50%,
+ transparent 0deg 2deg,
+ rgba(220, 38, 38, 0.05) 2deg 4deg
+ );
+ animation: rotate 20s linear infinite;
+ pointer-events: none;
+}
+
+@keyframes rotate {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+/* Header Arena */
+.arena-header {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+ margin-bottom: 20px;
+ position: relative;
+}
+
+.arena-title {
+ font-family: 'Orbitron', monospace;
+ font-size: 16px;
+ font-weight: 900;
+ color: #ffd700;
+ letter-spacing: 3px;
+ text-shadow:
+ 0 0 10px #ffd700,
+ 0 0 20px #ffd700,
+ 0 0 30px #ffd700;
+ animation: titleGlow 2s ease-in-out infinite alternate;
+}
+
+@keyframes titleGlow {
+ from { text-shadow: 0 0 10px #ffd700, 0 0 20px #ffd700, 0 0 30px #ffd700; }
+ to { text-shadow: 0 0 20px #ffd700, 0 0 30px #ffd700, 0 0 40px #ffd700; }
+}
+
+.arena-pulse {
+ width: 10px;
+ height: 10px;
+ background: #dc2626;
+ border-radius: 50%;
+ position: relative;
+ animation: pulse-danger 1s infinite;
+}
+
+.arena-pulse::before {
+ content: '';
+ position: absolute;
+ top: -5px;
+ left: -5px;
+ right: -5px;
+ bottom: -5px;
+ background: #dc2626;
+ border-radius: 50%;
+ opacity: 0.3;
+ animation: pulse-ring 1s infinite;
+}
+
+@keyframes pulse-danger {
+ 0%, 100% { transform: scale(1); }
+ 50% { transform: scale(1.2); }
+}
+
+@keyframes pulse-ring {
+ 0% { transform: scale(0.8); opacity: 0.8; }
+ 100% { transform: scale(2); opacity: 0; }
+}
+
+/* Combat Grid */
+.combat-grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 16px;
+}
+
+.combat-stat {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ padding: 16px;
+ background:
+ linear-gradient(90deg,
+ rgba(0, 0, 0, 0.8) 0%,
+ rgba(26, 26, 46, 0.9) 50%,
+ rgba(0, 0, 0, 0.8) 100%
+ );
+ border: 1px solid rgba(255, 215, 0, 0.3);
+ border-radius: 8px;
+ position: relative;
+ transition: all 0.3s ease;
+ cursor: pointer;
+}
+
+.combat-stat:hover {
+ transform: translateX(8px);
+ border-color: #ffd700;
+ box-shadow:
+ 0 4px 20px rgba(255, 215, 0, 0.3),
+ inset 0 1px 0 rgba(255, 215, 0, 0.1);
+}
+
+/* Icons animés */
+.stat-icon {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 48px;
+ height: 48px;
+ font-size: 24px;
+}
+
+.location-radar {
+ position: absolute;
+ width: 40px;
+ height: 40px;
+ border: 2px solid #10b981;
+ border-radius: 50%;
+ border-top-color: transparent;
+ animation: radar-spin 2s linear infinite;
+}
+
+.referee-badge {
+ position: absolute;
+ width: 35px;
+ height: 35px;
+ background: linear-gradient(45deg, #ffd700, #ffed4a);
+ clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
+ animation: badge-pulse 3s ease-in-out infinite;
+}
+
+.time-ring {
+ position: absolute;
+ width: 36px;
+ height: 36px;
+ border: 3px solid #dc2626;
+ border-radius: 50%;
+ border-left-color: transparent;
+ animation: time-tick 1s linear infinite;
+}
+
+@keyframes radar-spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+@keyframes badge-pulse {
+ 0%, 100% { transform: scale(1) rotate(0deg); }
+ 50% { transform: scale(1.1) rotate(5deg); }
+}
+
+@keyframes time-tick {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+/* Content */
+.stat-content {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+}
+
+.stat-label {
+ font-family: 'Orbitron', monospace;
+ font-size: 10px;
+ font-weight: 700;
+ color: #6b7280;
+ letter-spacing: 1px;
+ text-transform: uppercase;
+}
+
+.stat-value {
+ font-size: 16px;
+ font-weight: 700;
+ color: #ffffff;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
+}
+
+.stat-sub {
+ font-size: 11px;
+ color: #9ca3af;
+ font-weight: 500;
+}
+
+/* Glows spécifiques */
+.venue .stat-value { color: #10b981; }
+.referee .stat-value { color: #ffd700; }
+.timing .stat-value { color: #dc2626; }
+
+.stat-glow {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ border-radius: 8px;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ pointer-events: none;
+}
+
+.combat-stat:hover .stat-glow {
+ opacity: 1;
+}
+
+.venue-glow { box-shadow: inset 0 0 20px rgba(16, 185, 129, 0.2); }
+.referee-glow { box-shadow: inset 0 0 20px rgba(255, 215, 0, 0.2); }
+.timing-glow { box-shadow: inset 0 0 20px rgba(220, 38, 38, 0.2); }
+
+/* Footer Arena */
+.arena-footer {
+ margin-top: 20px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+}
+
+.danger-tape {
+ flex: 1;
+ height: 4px;
+ background:
+ repeating-linear-gradient(
+ 45deg,
+ #ffd700 0px,
+ #ffd700 10px,
+ #dc2626 10px,
+ #dc2626 20px
+ );
+ animation: tape-move 2s linear infinite;
+}
+
+@keyframes tape-move {
+ 0% { background-position: 0px 0px; }
+ 100% { background-position: 28px 0px; }
+}
+
+.warning-text {
+ font-family: 'Orbitron', monospace;
+ font-size: 12px;
+ font-weight: 700;
+ color: #ffd700;
+ letter-spacing: 1px;
+ animation: warning-blink 1.5s ease-in-out infinite alternate;
+}
+
+@keyframes warning-blink {
+ 0% { opacity: 1; }
+ 100% { opacity: 0.6; }
+}
+
+/* Responsive */
+@media (min-width: 768px) {
+ .combat-grid {
+ grid-template-columns: repeat(3, 1fr);
+ }
+
+ .combat-stat {
+ flex-direction: column;
+ text-align: center;
+ gap: 12px;
+ }
+
+ .combat-stat:hover {
+ transform: translateY(-4px);
+ }
+}
+
diff --git a/FencerJudgeFront/src/app/components/home/home.component.html b/FencerJudgeFront/src/app/components/home/home.component.html
index f761df7..402f9ff 100644
--- a/FencerJudgeFront/src/app/components/home/home.component.html
+++ b/FencerJudgeFront/src/app/components/home/home.component.html
@@ -1 +1,91 @@
-
+
+
+

+
+
+
+
+
+
+
+
Martin DUBOIS
+
CE Paris
+
12
+
+
+
+
+
+
Sophie BERNARD
+
Escrime Lyon
+
8
+
+
+
+
+
+
+
+
+
+
+ BATTLEFIELD
+ Gymnase Jean Moulin
+ PARIS • ZONE A
+
+
+
+
+
+
+
+ COMBAT JUDGE
+ Pierre MARTIN
+ OFFICIAL REFEREE
+
+
+
+
+
+
+
+ COMBAT START
+ 14:30
+ LIVE BATTLE
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FencerJudgeFront/src/assets/duel.webp b/FencerJudgeFront/src/assets/duel.webp
new file mode 100644
index 0000000000000000000000000000000000000000..5282448969320d77148555763399e2f94907e192
GIT binary patch
literal 174832
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