From f0fd146c547723d5d5923433a34f8132eb147287 Mon Sep 17 00:00:00 2001 From: ExostFlash Date: Tue, 1 Apr 2025 15:11:17 +0200 Subject: [PATCH] Add API, and object Article for api --- package-lock.json | 7 ++ package.json | 1 + src/app/app.module.ts | 9 ++- src/app/component/blogs/blogs.component.css | 58 +++++++++++++++ src/app/component/blogs/blogs.component.html | 17 ++++- src/app/component/blogs/blogs.component.ts | 13 ++-- .../component/contact/contact.component.css | 32 +++++++++ .../component/contact/contact.component.html | 71 ++++++++++++++++++- .../essential/footer/footer.component.html | 4 +- .../all/home-actu/home-actu.component.css | 54 ++++++++++++++ .../all/home-actu/home-actu.component.html | 41 +++++++++++ .../all/home-actu/home-actu.component.spec.ts | 23 ++++++ .../home/all/home-actu/home-actu.component.ts | 22 ++++++ src/app/component/home/home.component.html | 1 + src/app/interface/news.ts | 21 ++++++ src/app/service/all/news/news.service.spec.ts | 16 +++++ src/app/service/all/news/news.service.ts | 44 ++++++++++++ src/app/service/data.service.ts | 8 ++- tsconfig.json | 3 +- 19 files changed, 432 insertions(+), 13 deletions(-) create mode 100644 src/app/component/home/all/home-actu/home-actu.component.css create mode 100644 src/app/component/home/all/home-actu/home-actu.component.html create mode 100644 src/app/component/home/all/home-actu/home-actu.component.spec.ts create mode 100644 src/app/component/home/all/home-actu/home-actu.component.ts create mode 100644 src/app/interface/news.ts create mode 100644 src/app/service/all/news/news.service.spec.ts create mode 100644 src/app/service/all/news/news.service.ts diff --git a/package-lock.json b/package-lock.json index f89f351..0aabb3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@angular/router": "^19.2.0", "bootstrap": "^5.3.3", "bootstrap-icons": "^1.11.3", + "file-saver": "^2.0.5", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.15.0" @@ -7737,6 +7738,12 @@ "node": ">=0.8.0" } }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==", + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", diff --git a/package.json b/package.json index 6408a0c..9f33136 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@angular/router": "^19.2.0", "bootstrap": "^5.3.3", "bootstrap-icons": "^1.11.3", + "file-saver": "^2.0.5", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.15.0" diff --git a/src/app/app.module.ts b/src/app/app.module.ts index d878e3d..45ea6c1 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -4,6 +4,7 @@ import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { HttpClientModule } from '@angular/common/http'; /* Essential */ import { NavBarComponent } from './component/essential/nav-bar/nav-bar.component'; @@ -11,6 +12,7 @@ import { FooterComponent } from './component/essential/footer/footer.component'; /* Home */ import { HomeComponent } from './component/home/home.component'; +import { HomeActuComponent } from './component/home/all/home-actu/home-actu.component'; import { HomeAccueilComponent } from './component/home/all/home-accueil/home-accueil.component'; import { HomeSymptomsComponent } from './component/home/all/home-symptoms/home-symptoms.component'; @@ -24,7 +26,6 @@ import { BlogsComponent } from './component/blogs/blogs.component'; import { ContactComponent } from './component/contact/contact.component'; - @NgModule({ declarations: [ AppComponent, @@ -33,6 +34,7 @@ import { ContactComponent } from './component/contact/contact.component'; FooterComponent, /* Home */ HomeComponent, + HomeActuComponent, HomeAccueilComponent, HomeSymptomsComponent, /* Symptoms */ @@ -40,13 +42,14 @@ import { ContactComponent } from './component/contact/contact.component'; /* Blogs */ BlogsComponent, /* Contact */ - ContactComponent + ContactComponent ], imports: [ BrowserModule, AppRoutingModule, FormsModule, - ReactiveFormsModule + ReactiveFormsModule, + HttpClientModule ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/component/blogs/blogs.component.css b/src/app/component/blogs/blogs.component.css index e69de29..eefda9f 100644 --- a/src/app/component/blogs/blogs.component.css +++ b/src/app/component/blogs/blogs.component.css @@ -0,0 +1,58 @@ +/* Card Image */ +.card-img-top { + height: 200px; + object-fit: cover; + width: 100%; + } + + /* Assurer une taille uniforme pour les cards */ + .card { + display: flex; + flex-direction: column; + height: 100%; + } + + /* Body de la card pour avoir une hauteur fixe et uniforme */ + .card-body { + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 1.25rem; + height: 100%; + } + + /* Titre de la card (limiter la taille du texte si trop long) */ + .card-title { + font-size: 1.2rem; + font-weight: bold; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + max-height: 2.5em; /* Limite la hauteur du titre */ + margin-bottom: 1rem; + } + + /* Description de la card (maximiser l'espace disponible pour la description) */ + .card-text { + font-size: 0.9rem; + color: #6c757d; + flex-grow: 1; /* Permet à la description de prendre l'espace restant */ + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-box-orient: vertical; + margin-bottom: 1.5rem; /* Espacement entre la description et le bouton */ + } + + /* Bouton */ + .btn-primary { + background-color: #007bff; + border-color: #007bff; + text-align: center; + } + + .btn-primary:hover { + background-color: #0056b3; + border-color: #0056b3; + } + \ No newline at end of file diff --git a/src/app/component/blogs/blogs.component.html b/src/app/component/blogs/blogs.component.html index 11235a6..626840e 100644 --- a/src/app/component/blogs/blogs.component.html +++ b/src/app/component/blogs/blogs.component.html @@ -1 +1,16 @@ -

blogs works!

+
+
+
+ Article image +
+
{{ article.title }}
+

{{ article.description }}

+ Lire l'article +
+
+
+
+ + \ No newline at end of file diff --git a/src/app/component/blogs/blogs.component.ts b/src/app/component/blogs/blogs.component.ts index 7c77df9..27dc0f4 100644 --- a/src/app/component/blogs/blogs.component.ts +++ b/src/app/component/blogs/blogs.component.ts @@ -1,6 +1,7 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { DataService } from '@services/data.service'; +import { Article } from '@interface/news'; @Component({ selector: 'app-blogs', @@ -8,10 +9,14 @@ import { DataService } from '@services/data.service'; templateUrl: './blogs.component.html', styleUrl: './blogs.component.css' }) -export class BlogsComponent { +export class BlogsComponent implements OnInit { + articles: Article[] = []; + constructor(private dataService: DataService) {} - get blogs() { - return this.dataService.getBlogs(); + ngOnInit(): void { + this.dataService.getNews().subscribe((response) => { + this.articles = response.articles; + }); } } diff --git a/src/app/component/contact/contact.component.css b/src/app/component/contact/contact.component.css index e69de29..ea16378 100644 --- a/src/app/component/contact/contact.component.css +++ b/src/app/component/contact/contact.component.css @@ -0,0 +1,32 @@ +/* Amélioration du formulaire */ +.custom-input { + border: 1px solid #ced4da; + border-radius: 8px; + padding: 10px; + transition: border-color 0.3s ease-in-out, box-shadow 0.2s ease-in-out; +} + +.custom-input:focus { + border-color: #007bff; + box-shadow: 0 0 5px rgba(0, 123, 255, 0.2); + outline: none; +} + +/* Bouton amélioré */ +.btn-primary { + background-color: #007bff; + border: none; + font-weight: 500; + transition: background 0.3s ease, transform 0.2s ease-in-out; +} + +.btn-primary:hover { + background-color: #0056b3; + transform: scale(1.02); +} + +/* Liste des centres */ +.contact-card { + background: #f8f9fa; + border-radius: 10px; +} diff --git a/src/app/component/contact/contact.component.html b/src/app/component/contact/contact.component.html index 9cae746..4b2fb60 100644 --- a/src/app/component/contact/contact.component.html +++ b/src/app/component/contact/contact.component.html @@ -1 +1,70 @@ -

contact works!

+
+
+

Contactez-nous

+

Vous avez une question ? Contactez-nous via le formulaire ou rendez-vous dans l'un de nos centres.

+
+ +
+ +
+
+
+

Nos Centres

+
    +
  • + +
    +
    {{ address.title }}
    +

    Type : {{ address.type }}

    +

    Adresse : {{ address.address }}

    +
    +
  • +
+
+
+
+ + +
+
+
+

Laissez-nous un message

+
+
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ + +
+ + +
+
+
+
+
+
diff --git a/src/app/component/essential/footer/footer.component.html b/src/app/component/essential/footer/footer.component.html index 8f79ba5..8853338 100644 --- a/src/app/component/essential/footer/footer.component.html +++ b/src/app/component/essential/footer/footer.component.html @@ -19,8 +19,8 @@ Mentions légales Cookies Accessibilité - Nous contacter - Presse + Nous contacter + Presse diff --git a/src/app/component/home/all/home-actu/home-actu.component.css b/src/app/component/home/all/home-actu/home-actu.component.css new file mode 100644 index 0000000..4d0ecd9 --- /dev/null +++ b/src/app/component/home/all/home-actu/home-actu.component.css @@ -0,0 +1,54 @@ +#newsCarousel { + position: relative; + } + + .carousel-item img { + width: 100%; + height: 350px; /* Ajuste la hauteur de l'image pour avoir une vue cohérente */ + object-fit: cover; + border-radius: 10px; + } + + .carousel-caption { + position: absolute; + bottom: 20px; + background-color: rgba(13, 110, 253, 0.7); /* Légère transparence pour le fond */ + padding: 15px; + border-radius: 5px; + color: white; + } + + .carousel-caption h5 { + font-size: 1.5rem; + font-weight: bold; + } + + .carousel-caption p { + font-size: 1rem; + margin-top: 10px; + } + + .carousel-caption .btn { + margin-top: 10px; + } + + .carousel-control-prev, + .carousel-control-next { + background-color: transparent; /* Pas de fond sombre */ + border-radius: 50%; /* Forme ronde */ + border: none; /* Retirer la bordure */ + } + + .carousel-control-prev:hover, + .carousel-control-next:hover { + background-color: transparent; /* Pas de changement de fond au survol */ + } + + .carousel-control-next-icon-new svg { + transform: rotate(180deg); /* Rotation de 180° */ + } + + .carousel-control-prev-icon-new svg, + .carousel-control-next-icon-new svg { + fill: #0d95fd; /* Couleur de l'icône */ + } \ No newline at end of file diff --git a/src/app/component/home/all/home-actu/home-actu.component.html b/src/app/component/home/all/home-actu/home-actu.component.html new file mode 100644 index 0000000..745189e --- /dev/null +++ b/src/app/component/home/all/home-actu/home-actu.component.html @@ -0,0 +1,41 @@ + + \ No newline at end of file diff --git a/src/app/component/home/all/home-actu/home-actu.component.spec.ts b/src/app/component/home/all/home-actu/home-actu.component.spec.ts new file mode 100644 index 0000000..4097df3 --- /dev/null +++ b/src/app/component/home/all/home-actu/home-actu.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeActuComponent } from './home-actu.component'; + +describe('HomeActuComponent', () => { + let component: HomeActuComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [HomeActuComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(HomeActuComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/component/home/all/home-actu/home-actu.component.ts b/src/app/component/home/all/home-actu/home-actu.component.ts new file mode 100644 index 0000000..dea9ee0 --- /dev/null +++ b/src/app/component/home/all/home-actu/home-actu.component.ts @@ -0,0 +1,22 @@ +import { Component, OnInit } from '@angular/core'; + +import { DataService } from '@services/data.service'; +import { Article } from '@interface/news'; + +@Component({ + selector: 'app-home-actu', + standalone: false, + templateUrl: './home-actu.component.html', + styleUrls: ['./home-actu.component.css'] // Correction ici : styleUrls (pluriel) +}) +export class HomeActuComponent implements OnInit { + articles: Article[] = []; + + constructor(private dataService: DataService) {} + + ngOnInit(): void { + this.dataService.getNews().subscribe((response) => { + this.articles = response.articles.slice(0, 5); + }); + } +} diff --git a/src/app/component/home/home.component.html b/src/app/component/home/home.component.html index 0696d1b..bb4d98d 100644 --- a/src/app/component/home/home.component.html +++ b/src/app/component/home/home.component.html @@ -1,4 +1,5 @@
+
\ No newline at end of file diff --git a/src/app/interface/news.ts b/src/app/interface/news.ts new file mode 100644 index 0000000..faf1ef8 --- /dev/null +++ b/src/app/interface/news.ts @@ -0,0 +1,21 @@ +export interface Source { + id: string | null; + name: string; +} + +export interface Article { + source: Source; + author: string; + title: string; + description: string; + url: string; + urlToImage: string; + publishedAt: string; + content: string; +} + +export interface NewsResponse { + status: string; + totalResults: number; + articles: Article[]; +} \ No newline at end of file diff --git a/src/app/service/all/news/news.service.spec.ts b/src/app/service/all/news/news.service.spec.ts new file mode 100644 index 0000000..d129c15 --- /dev/null +++ b/src/app/service/all/news/news.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { NewsService } from './news.service'; + +describe('NewsService', () => { + let service: NewsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(NewsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/service/all/news/news.service.ts b/src/app/service/all/news/news.service.ts new file mode 100644 index 0000000..a666e80 --- /dev/null +++ b/src/app/service/all/news/news.service.ts @@ -0,0 +1,44 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { NewsResponse } from '@interface/news'; // Assure-toi que le chemin est correct +import { tap } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class NewsService { + private apiKey: string = 'e9bb2770d3374edea421bcadbecdca5c'; + private apiUrl: string = `https://newsapi.org/v2/everything?q=covid&language=fr&apiKey=${this.apiKey}`; + private storageKey: string = 'newsData'; // Clé utilisée pour le localStorage + private lastUpdateKey: string = 'lastUpdate'; // Clé pour stocker la date de la dernière mise à jour + private cacheDuration: number = 60 * 60 * 1000; // Durée en millisecondes (par exemple, 1 heure) + + constructor(private http: HttpClient) {} + + // Méthode pour récupérer les articles de l'API ou du localStorage + getNews(): Observable { + const storedData = localStorage.getItem(this.storageKey); + const lastUpdate = localStorage.getItem(this.lastUpdateKey); + + // Vérifie si les données existent et si elles sont encore valides + const currentTime = new Date().getTime(); + + if (storedData && lastUpdate && (currentTime - Number(lastUpdate)) < this.cacheDuration) { + // Si les données sont présentes et récentes (moins de 1 heure par exemple), on les retourne + return new Observable(observer => { + observer.next(JSON.parse(storedData)); // On envoie les données stockées + observer.complete(); + }); + } else { + // Sinon, on effectue la requête à l'API + return this.http.get(this.apiUrl).pipe( + tap(response => { + // On stocke la réponse dans le localStorage et la date de mise à jour + localStorage.setItem(this.storageKey, JSON.stringify(response)); + localStorage.setItem(this.lastUpdateKey, currentTime.toString()); // On enregistre l'heure de la mise à jour + }) + ); + } + } +} \ No newline at end of file diff --git a/src/app/service/data.service.ts b/src/app/service/data.service.ts index f35d00a..3ff8296 100644 --- a/src/app/service/data.service.ts +++ b/src/app/service/data.service.ts @@ -3,6 +3,7 @@ import { Injectable } from '@angular/core'; import { SymptomService } from './all/symptom/symptom.service'; import { BlogService } from './all/blog/blog.service'; import { AddressService } from './all/address/address.service'; +import { NewsService } from './all/news/news.service'; @Injectable({ providedIn: 'root' @@ -12,7 +13,8 @@ export class DataService { constructor( private symptomService: SymptomService, private blogService: BlogService, - private addressService: AddressService + private addressService: AddressService, + private newsService: NewsService ) {} getSymptoms() { @@ -26,4 +28,8 @@ export class DataService { getAddresses() { return this.addressService.getAddresses(); } + + getNews() { + return this.newsService.getNews(); + } } diff --git a/tsconfig.json b/tsconfig.json index 1686a50..8fdd6ae 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,7 +19,8 @@ "module": "ES2022", "baseUrl": "./src", "paths": { - "@services/*": ["app/service/*"] + "@services/*": ["app/service/*"], + "@interface/*": ["app/interface/*"] } }, "angularCompilerOptions": {