Merge pull request 'feature/06' (#6) from feature/06 into develop

Reviewed-on: https://git.gitpushf.uk/SchoolTask/angular-covid/pulls/6
This commit is contained in:
ExostFlash 2025-04-01 13:12:52 +00:00
commit ff60c3a53f
59 changed files with 857 additions and 134 deletions

7
package-lock.json generated
View file

@ -17,6 +17,7 @@
"@angular/router": "^19.2.0", "@angular/router": "^19.2.0",
"bootstrap": "^5.3.3", "bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3", "bootstrap-icons": "^1.11.3",
"file-saver": "^2.0.5",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.15.0" "zone.js": "~0.15.0"
@ -7737,6 +7738,12 @@
"node": ">=0.8.0" "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": { "node_modules/fill-range": {
"version": "7.1.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",

View file

@ -19,6 +19,7 @@
"@angular/router": "^19.2.0", "@angular/router": "^19.2.0",
"bootstrap": "^5.3.3", "bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3", "bootstrap-icons": "^1.11.3",
"file-saver": "^2.0.5",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.15.0" "zone.js": "~0.15.0"

View file

@ -1,16 +1,17 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { ContactComponent } from './contact/contact.component'; import { HomeComponent } from './component/home/home.component';
import { SymptomeComponent } from './symptome/symptome.component'; import { SymptomsComponent } from './component/symptoms/symptoms.component';
import { BlogComponent } from './blog/blog.component'; import { BlogsComponent } from './component/blogs/blogs.component';
import { ContactComponent } from './component/contact/contact.component';
const routes: Routes = [ const routes: Routes = [
{ path: '', component: HomeComponent }, { path: '', component: HomeComponent },
{ path: 'home', component: HomeComponent }, { path: 'home', component: HomeComponent },
{ path: 'contacts', component: ContactComponent }, { path: 'symptoms', component: SymptomsComponent },
{ path: 'symptômes', component: SymptomeComponent }, { path: 'blogs', component: BlogsComponent },
{ path: 'blog', component: BlogComponent }, { path: 'contact', component: ContactComponent },
]; ];
@NgModule({ @NgModule({

View file

@ -4,29 +4,52 @@ import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NavBarComponent } from './nav-bar/nav-bar.component'; import { HttpClientModule } from '@angular/common/http';
import { HomeComponent } from './home/home.component';
import { FooterComponent } from './footer/footer.component'; /* Essential */
import { SymptomeComponent } from './symptome/symptome.component'; import { NavBarComponent } from './component/essential/nav-bar/nav-bar.component';
import { ContactComponent } from './contact/contact.component'; import { FooterComponent } from './component/essential/footer/footer.component';
import { BlogComponent } from './blog/blog.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';
/* Symptoms */
import { SymptomsComponent } from './component/symptoms/symptoms.component';
/* Blogs */
import { BlogsComponent } from './component/blogs/blogs.component';
/* Contact */
import { ContactComponent } from './component/contact/contact.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
AppComponent, AppComponent,
/* Essential */
NavBarComponent, NavBarComponent,
HomeComponent,
FooterComponent, FooterComponent,
SymptomeComponent, /* Home */
ContactComponent, HomeComponent,
BlogComponent HomeActuComponent,
HomeAccueilComponent,
HomeSymptomsComponent,
/* Symptoms */
SymptomsComponent,
/* Blogs */
BlogsComponent,
/* Contact */
ContactComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
AppRoutingModule, AppRoutingModule,
FormsModule, FormsModule,
ReactiveFormsModule ReactiveFormsModule,
HttpClientModule
], ],
providers: [], providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]

View file

@ -1 +0,0 @@
<p>blog works!</p>

View file

@ -1,11 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-blog',
standalone: false,
templateUrl: './blog.component.html',
styleUrl: './blog.component.css'
})
export class BlogComponent {
}

View file

@ -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;
}

View file

@ -0,0 +1,16 @@
<div class="row row-cols-1 row-cols-md-3 g-4 mb-4">
<div *ngFor="let article of articles" class="col">
<div class="card h-100">
<img [src]="article.urlToImage || 'assets/default.jpg'" class="card-img-top" alt="Article image" loading="lazy">
<div class="card-body">
<h5 class="card-title">{{ article.title }}</h5>
<p class="card-text">{{ article.description }}</p>
<a [href]="article.url" target="_blank" class="btn btn-primary">Lire l'article</a>
</div>
</div>
</div>
</div>
<div *ngIf="articles.length === 0" class="alert alert-warning" role="alert">
Aucune actualité disponible pour le moment.
</div>

View file

@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BlogComponent } from './blog.component'; import { BlogsComponent } from './blogs.component';
describe('BlogComponent', () => { describe('BlogsComponent', () => {
let component: BlogComponent; let component: BlogsComponent;
let fixture: ComponentFixture<BlogComponent>; let fixture: ComponentFixture<BlogsComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [BlogComponent] declarations: [BlogsComponent]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(BlogComponent); fixture = TestBed.createComponent(BlogsComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });

View file

@ -0,0 +1,22 @@
import { Component, OnInit } from '@angular/core';
import { DataService } from '@services/data.service';
import { Article } from '@interface/news';
@Component({
selector: 'app-blogs',
standalone: false,
templateUrl: './blogs.component.html',
styleUrl: './blogs.component.css'
})
export class BlogsComponent implements OnInit {
articles: Article[] = [];
constructor(private dataService: DataService) {}
ngOnInit(): void {
this.dataService.getNews().subscribe((response) => {
this.articles = response.articles;
});
}
}

View file

@ -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;
}

View file

@ -0,0 +1,70 @@
<div class="container mt-5">
<div class="text-center mb-5">
<h2 class="fw-bold text-primary">Contactez-nous</h2>
<p class="text-muted">Vous avez une question ? Contactez-nous via le formulaire ou rendez-vous dans l'un de nos centres.</p>
</div>
<div class="row g-4 mb-3">
<!-- Liste des centres -->
<div class="col-lg-6 mb-3">
<div class="card contact-card shadow-lg border-0 rounded-4">
<div class="card-body">
<h4 class="card-title text-primary mb-4">Nos Centres</h4>
<ul class="list-group list-group-flush">
<li class="list-group-item py-3 d-flex align-items-start" *ngFor="let address of addresses">
<i class="bi bi-geo-alt-fill text-primary me-3 fs-4"></i>
<div>
<h5 class="mb-1">{{ address.title }}</h5>
<p class="text-muted mb-1"><strong>Type :</strong> {{ address.type }}</p>
<p class="text-muted mb-0"><strong>Adresse :</strong> {{ address.address }}</p>
</div>
</li>
</ul>
</div>
</div>
</div>
<!-- Formulaire de contact -->
<div class="col-lg-6 mb-3">
<div class="card contact-form shadow-lg border-0 rounded-4">
<div class="card-body p-4">
<h4 class="card-title text-primary mb-4">Laissez-nous un message</h4>
<form>
<div class="mb-3">
<label for="name" class="form-label">Nom</label>
<div class="input-group">
<span class="input-group-text bg-light"><i class="bi bi-person-fill"></i></span>
<input type="text" class="form-control custom-input" id="name" placeholder="Votre nom">
</div>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<div class="input-group">
<span class="input-group-text bg-light"><i class="bi bi-envelope-fill"></i></span>
<input type="email" class="form-control custom-input" id="email" placeholder="Votre email">
</div>
</div>
<div class="mb-3">
<label for="subject" class="form-label">Sujet</label>
<div class="input-group">
<span class="input-group-text bg-light"><i class="bi bi-chat-left-text-fill"></i></span>
<input type="text" class="form-control custom-input" id="subject" placeholder="Sujet de votre message">
</div>
</div>
<div class="mb-3">
<label for="message" class="form-label">Message</label>
<textarea class="form-control custom-input" id="message" rows="4" placeholder="Votre message"></textarea>
</div>
<button type="submit" class="btn btn-primary w-100 py-2 rounded-pill custom-btn">
<i class="bi bi-send-fill"></i> Envoyer
</button>
</form>
</div>
</div>
</div>
</div>
</div>

View file

@ -1,5 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { DataService } from '@services/data.service';
@Component({ @Component({
selector: 'app-contact', selector: 'app-contact',
standalone: false, standalone: false,
@ -7,5 +9,9 @@ import { Component } from '@angular/core';
styleUrl: './contact.component.css' styleUrl: './contact.component.css'
}) })
export class ContactComponent { export class ContactComponent {
constructor(private dataService: DataService) {}
get addresses() {
return this.dataService.getAddresses();
}
} }

View file

@ -19,8 +19,8 @@
<a href="#" class="text-white me-3 SpaceMargin-02">Mentions légales</a> <a href="#" class="text-white me-3 SpaceMargin-02">Mentions légales</a>
<a href="#" class="text-white me-3">Cookies</a> <a href="#" class="text-white me-3">Cookies</a>
<a href="#" class="text-white me-3">Accessibilité</a> <a href="#" class="text-white me-3">Accessibilité</a>
<a routerLink="/contacts" class="text-white me-3">Nous contacter</a> <a routerLink="/contact" class="text-white me-3">Nous contacter</a>
<a href="#" class="text-white me-3">Presse</a> <a routerLink="/blogs" class="text-white me-3">Presse</a>
</div> </div>
</div> </div>
</footer> </footer>

View file

@ -9,22 +9,21 @@
<ul class="navbar-nav ms-auto"> <ul class="navbar-nav ms-auto">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" <a class="nav-link"
routerLink="/" routerLink="/"
routerLinkActive="active-link" routerLinkActive="active-link"
[routerLinkActiveOptions]="{ exact: isHomeActiveBool() }" [routerLinkActiveOptions]="{ exact: isHomeActiveBool() }"
[class.active-link]="isHomeActive()"> [class.active-link]="isHomeActive()">
Accueil Accueil
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" routerLink="/symptômes" routerLinkActive="active-link">Symptômes</a> <a class="nav-link" routerLink="/symptoms" routerLinkActive="active-link">Symptômes</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" routerLink="/blog" routerLinkActive="active-link">Blog</a> <a class="nav-link" routerLink="/blogs" routerLinkActive="active-link">Blog</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" routerLink="/contacts" routerLinkActive="active-link">Contacts</a> <a class="nav-link" routerLink="/contact" routerLinkActive="active-link">Contact</a>
</li> </li>
</ul> </ul>
</div> </div>

View file

@ -0,0 +1,10 @@
<div class="row align-items-center mb-4">
<div class="col-md-6 text-center text-md-start">
<h2 class="text-primary">Ensemble. Luttons.</h2>
<p>Lorem, ipsum dolor, sit amet consectetur adipisicing elit...</p>
<button routerLink="/symptoms" class="btn btn-primary">Comment se protéger</button>
</div>
<div class="col-md-6 text-center">
<img src="/assets/illustration.png" alt="Illustration Covid" class="img-fluid">
</div>
</div>

View file

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HomeAccueilComponent } from './home-accueil.component';
describe('HomeAccueilComponent', () => {
let component: HomeAccueilComponent;
let fixture: ComponentFixture<HomeAccueilComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [HomeAccueilComponent]
})
.compileComponents();
fixture = TestBed.createComponent(HomeAccueilComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-home-accueil',
standalone: false,
templateUrl: './home-accueil.component.html',
styleUrl: './home-accueil.component.css'
})
export class HomeAccueilComponent {
}

View file

@ -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 */
}

View file

@ -0,0 +1,41 @@
<div *ngIf="articles.length > 0" id="newsCarousel" class="carousel slide mb-4" data-bs-ride="carousel">
<div class="carousel-indicators">
<button *ngFor="let article of articles; let i = index"
type="button" data-bs-target="#newsCarousel"
[attr.data-bs-slide-to]="i"
[class.active]="i === 0"
[attr.aria-current]="i === 0 ? 'true' : null"
[attr.aria-label]="'Slide ' + (i + 1)">
</button>
</div>
<div class="carousel-inner">
<div *ngFor="let article of articles; let i = index" class="carousel-item" [class.active]="i === 0">
<img [src]="article.urlToImage || 'assets/default.jpg'" loading="lazy" class="d-block w-100" [alt]="article.title">
<div class="carousel-caption d-none d-md-block text-start">
<h5 style="color: black;">{{ article.title }}</h5>
<p>{{ article.description }}</p>
</div>
</div>
</div>
<button class="carousel-control-prev" type="button" data-bs-target="#newsCarousel" data-bs-slide="prev">
<span class="carousel-control-prev-icon-new" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 16 16">
<path d="M11.854 1.146a.5.5 0 0 1 0 .708L5.707 8l6.147 6.146a.5.5 0 1 1-.708.708l-6.5-6.5a.5.5 0 0 1 0-.708l6.5-6.5a.5.5 0 0 1 .708 0z"/>
</svg>
</span>
<span class="visually-hidden">Précédent</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#newsCarousel" data-bs-slide="next">
<span class="carousel-control-next-icon-new" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 16 16">
<path d="M11.854 1.146a.5.5 0 0 1 0 .708L5.707 8l6.147 6.146a.5.5 0 1 1-.708.708l-6.5-6.5a.5.5 0 0 1 0-.708l6.5-6.5a.5.5 0 0 1 .708 0z"/>
</svg>
</span>
<span class="visually-hidden">Suivant</span>
</button>
</div>
<div *ngIf="articles.length === 0" class="alert alert-warning mb-4" role="alert">
Aucune actualité disponible pour le moment.
</div>

View file

@ -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<HomeActuComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [HomeActuComponent]
})
.compileComponents();
fixture = TestBed.createComponent(HomeActuComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -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);
});
}
}

View file

@ -0,0 +1,18 @@
<div class="text-center mb-4">
<h2 class="text-primary">Symptôme du Coronavirus</h2>
<p>Lorem, ipsum dolor sit amet consectetur...</p>
</div>
<div class="row">
<div *ngFor="let symptom of symptoms | slice:0:4" class="col-md-6 mb-3">
<div class="card p-3">
<div class="d-flex align-items-center">
<img [src]="symptom.imageName" [alt]="symptom.title" class="me-3" style="width: 100px;">
<div>
<h5 class="text-primary">{{ symptom.title }}</h5>
<p>{{ symptom.description }}</p>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HomeSymptomsComponent } from './home-symptoms.component';
describe('HomeSymptomsComponent', () => {
let component: HomeSymptomsComponent;
let fixture: ComponentFixture<HomeSymptomsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [HomeSymptomsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(HomeSymptomsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,17 @@
import { Component } from '@angular/core';
import { DataService } from '@services/data.service';
@Component({
selector: 'app-home-symptoms',
standalone: false,
templateUrl: './home-symptoms.component.html',
styleUrl: './home-symptoms.component.css'
})
export class HomeSymptomsComponent {
constructor(private dataService: DataService) {}
get symptoms() {
return this.dataService.getSymptoms();
}
}

View file

@ -0,0 +1,5 @@
<div class="container py-5">
<app-home-actu></app-home-actu>
<app-home-accueil></app-home-accueil>
<app-home-symptoms></app-home-symptoms>
</div>

View file

@ -0,0 +1,18 @@
<div class="text-center mb-4">
<h2 class="text-primary">Symptôme du Coronavirus</h2>
<p>Lorem, ipsum dolor sit amet consectetur...</p>
</div>
<div class="row">
<div *ngFor="let symptom of symptoms" class="col-md-6 mb-3">
<div class="card p-3">
<div class="d-flex align-items-center">
<img [src]="symptom.imageName" [alt]="symptom.title" class="me-3" style="width: 100px;">
<div>
<h5 class="text-primary">{{ symptom.title }}</h5>
<p>{{ symptom.description }}</p>
</div>
</div>
</div>
</div>
</div>

View file

@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SymptomeComponent } from './symptome.component'; import { SymptomsComponent } from './symptoms.component';
describe('SymptomeComponent', () => { describe('SymptomsComponent', () => {
let component: SymptomeComponent; let component: SymptomsComponent;
let fixture: ComponentFixture<SymptomeComponent>; let fixture: ComponentFixture<SymptomsComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [SymptomeComponent] declarations: [SymptomsComponent]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(SymptomeComponent); fixture = TestBed.createComponent(SymptomsComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });

View file

@ -0,0 +1,17 @@
import { Component } from '@angular/core';
import { DataService } from '@services/data.service';
@Component({
selector: 'app-symptoms',
standalone: false,
templateUrl: './symptoms.component.html',
styleUrl: './symptoms.component.css'
})
export class SymptomsComponent {
constructor(private dataService: DataService) {}
get symptoms() {
return this.dataService.getSymptoms();
}
}

View file

@ -1 +0,0 @@
<p>contact works!</p>

View file

@ -1,63 +0,0 @@
<div class="container py-5">
<div class="row align-items-center mb-4">
<div class="col-md-6 text-center text-md-start">
<h2 class="text-primary">Ensemble. Luttons.</h2>
<p>Lorem, ipsum dolor, sit amet consectetur adipisicing elit...</p>
<button routerLink="/symptômes" class="btn btn-primary">Comment se protéger</button>
</div>
<div class="col-md-6 text-center">
<img src="/assets/illustration.png" alt="Illustration Covid" class="img-fluid">
</div>
</div>
<div class="text-center mb-4">
<h2 class="text-primary">Symptôme du Coronavirus</h2>
<p>Lorem, ipsum dolor sit amet consectetur...</p>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<div class="card p-3">
<div class="d-flex align-items-center">
<img src="/assets/fievre.png" alt="Forte fièvre" class="me-3">
<div>
<h5 class="text-primary">Forte fièvre</h5>
<p>Lorem ipsum dolor sit amet...</p>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card p-3">
<div class="d-flex align-items-center">
<img src="/assets/toux.png" alt="Toux" class="me-3">
<div>
<h5 class="text-primary">Toux</h5>
<p>Lorem ipsum dolor sit amet...</p>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card p-3">
<div class="d-flex align-items-center">
<img src="/assets/gorge.png" alt="Gorge irritée" class="me-3">
<div>
<h5 class="text-primary">Gorge irritée</h5>
<p>Lorem ipsum dolor sit amet...</p>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card p-3">
<div class="d-flex align-items-center">
<img src="/assets/migraine.png" alt="Migraine" class="me-3">
<div>
<h5 class="text-primary">Migraine</h5>
<p>Lorem ipsum dolor sit amet...</p>
</div>
</div>
</div>
</div>
</div>

21
src/app/interface/news.ts Normal file
View file

@ -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[];
}

View file

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { AddressService } from './address.service';
describe('AddressService', () => {
let service: AddressService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AddressService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View file

@ -0,0 +1,19 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class AddressService {
constructor() { }
private addresses = [
{ title: "Centre de Dépistage - Laboratoire Biomédical", type: "Dépistage", address: "15 Rue de la Colombette, 31000 Toulouse" },
{ title: "Centre de Vaccination Municipal Toulouse La Daurade", type: "Vaccination", address: "17 Place de la Daurade, 31000 Toulouse" },
{ title: "Pharmacie des Pyrénées", type: "Dépistage", address: "77 Boulevard de Strasbourg, 31000 Toulouse" },
{ title: "Centre de Vaccination - CHU Toulouse Purpan", type: "Dépistage & Vaccination", address: "1 Place du Docteur Joseph Baylac, 31300 Toulouse" }
];
getAddresses() {
return this.addresses;
}
}

View file

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { BlogService } from './blog.service';
describe('BlogService', () => {
let service: BlogService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(BlogService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View file

@ -0,0 +1,35 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class BlogService {
constructor() { }
private blogs = [
{
title: "Déclaration de la pandémie par l'OMS",
date: "2020-03-11",
text: "L'Organisation mondiale de la santé (OMS) a déclaré le COVID-19 comme une pandémie mondiale, soulignant la gravité et la rapidité de la propagation du virus."
},
{
title: "Premier confinement en France",
date: "2020-03-17",
text: "La France a instauré un confinement national strict pour limiter la propagation du virus, entraînant la fermeture des écoles, des commerces non essentiels et des restrictions de déplacement."
},
{
title: "Début de la campagne de vaccination en France",
date: "2020-12-27",
text: "La France a lancé sa campagne de vaccination contre le COVID-19, en commençant par les populations les plus vulnérables."
},
{
title: "Levée progressive des restrictions sanitaires",
date: "2021-06-09",
text: "La France a entamé une levée progressive des restrictions, avec la réouverture des restaurants, des lieux culturels et l'assouplissement du couvre-feu."
}
];
getBlogs() {
return this.blogs;
}
}

View file

@ -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();
});
});

View file

@ -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<NewsResponse> {
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<NewsResponse>(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
})
);
}
}
}

View file

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { SymptomService } from './symptom.service';
describe('SymptomService', () => {
let service: SymptomService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(SymptomService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View file

@ -0,0 +1,55 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class SymptomService {
constructor() { }
private symptoms = [
{
title: "Toux",
imageName: "/assets/toux.png",
description: "Une toux sèche et persistante est l'un des premiers symptômes du COVID-19. Elle peut être accompagnée de difficultés respiratoires."
},
{
title: "Fièvre",
imageName: "/assets/fievre.png",
description: "Une fièvre supérieure à 38°C est un symptôme courant du COVID-19, indiquant que le corps combat une infection."
},
{
title: "Fatigue",
imageName: "/assets/what.avif",
description: "Un état de fatigue intense, même sans activité physique, est fréquemment observé chez les personnes atteintes du virus."
},
{
title: "Perte du goût et de l'odorat",
imageName: "/assets/what.avif",
description: "L'anosmie (perte de l'odorat) et l'agueusie (perte du goût) sont des symptômes spécifiques souvent signalés par les patients atteints du COVID-19."
},
{
title: "Difficultés respiratoires",
imageName: "/assets/what.avif",
description: "Une sensation d'essoufflement ou une difficulté à respirer peut indiquer une forme plus grave de l'infection nécessitant une prise en charge médicale."
},
{
title: "Maux de gorge",
imageName: "/assets/gorge.png",
description: "Un mal de gorge accompagné d'une sensation de brûlure ou de picotements peut être un signe d'infection par le virus."
},
{
title: "Maux de tête",
imageName: "/assets/migraine.png",
description: "Des céphalées intenses et persistantes sont rapportées par de nombreux patients atteints du COVID-19."
},
{
title: "Douleurs musculaires",
imageName: "/assets/what.avif",
description: "Des douleurs musculaires généralisées peuvent accompagner l'infection, similaires à celles observées lors d'une grippe."
}
];
getSymptoms() {
return this.symptoms;
}
}

View file

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { DataService } from './data.service';
describe('DataService', () => {
let service: DataService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(DataService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View file

@ -0,0 +1,35 @@
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'
})
export class DataService {
constructor(
private symptomService: SymptomService,
private blogService: BlogService,
private addressService: AddressService,
private newsService: NewsService
) {}
getSymptoms() {
return this.symptomService.getSymptoms();
}
getBlogs() {
return this.blogService.getBlogs();
}
getAddresses() {
return this.addressService.getAddresses();
}
getNews() {
return this.newsService.getNews();
}
}

View file

@ -1 +0,0 @@
<p>symptome works!</p>

View file

@ -1,11 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-symptome',
standalone: false,
templateUrl: './symptome.component.html',
styleUrl: './symptome.component.css'
})
export class SymptomeComponent {
}

BIN
src/assets/what.avif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -16,7 +16,12 @@
"moduleResolution": "bundler", "moduleResolution": "bundler",
"importHelpers": true, "importHelpers": true,
"target": "ES2022", "target": "ES2022",
"module": "ES2022" "module": "ES2022",
"baseUrl": "./src",
"paths": {
"@services/*": ["app/service/*"],
"@interface/*": ["app/interface/*"]
}
}, },
"angularCompilerOptions": { "angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false, "enableI18nLegacyMessageIdFormat": false,