Inital Commit

This commit is contained in:
ExostFlash 2025-05-14 14:54:22 +02:00
commit 64ead91fc2
68 changed files with 16302 additions and 0 deletions

16
.editorconfig Normal file
View file

@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false

42
.gitignore vendored Normal file
View file

@ -0,0 +1,42 @@
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db

27
README.md Normal file
View file

@ -0,0 +1,27 @@
# SecondApp
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 18.2.3.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.

112
angular.json Normal file
View file

@ -0,0 +1,112 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"second-app": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"standalone": false
},
"@schematics/angular:directive": {
"standalone": false
},
"@schematics/angular:pipe": {
"standalone": false
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/second-app",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kB",
"maximumError": "4kB"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "second-app:build:production"
},
"development": {
"buildTarget": "second-app:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.css"
],
"scripts": []
}
}
}
}
},
"cli": {
"analytics": false
}
}

14653
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

42
package.json Normal file
View file

@ -0,0 +1,42 @@
{
"name": "second-app",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "json-server -- watch produit.json --port 3500 & ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "^18.2.0",
"@angular/common": "^18.2.0",
"@angular/compiler": "^18.2.0",
"@angular/core": "^18.2.0",
"@angular/forms": "^18.2.0",
"@angular/platform-browser": "^18.2.0",
"@angular/platform-browser-dynamic": "^18.2.0",
"@angular/router": "^18.2.0",
"bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3",
"rxjs": "~7.8.0",
"simple-datatables": "^9.2.1",
"sweetalert2": "^11.14.5",
"tslib": "^2.3.0",
"zone.js": "~0.14.10"
},
"devDependencies": {
"@angular-devkit/build-angular": "^18.2.3",
"@angular/cli": "^18.2.3",
"@angular/compiler-cli": "^18.2.0",
"@types/jasmine": "~5.1.0",
"jasmine-core": "~5.2.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.5.2"
}
}

95
produit.json Normal file
View file

@ -0,0 +1,95 @@
{
"produits": [
{
"id": "1",
"name": "Produit 1",
"price": 100,
"quantity": 5,
"checked": true
},
{
"id": "2",
"name": "Produit 2",
"price": 200,
"quantity": 5,
"checked": false
},
{
"id": "3",
"name": "Produit 3",
"price": 300,
"quantity": 5,
"checked": true
},
{
"id": "4",
"name": "Produit 4",
"price": 400,
"quantity": 5,
"checked": false
},
{
"id": "5",
"name": "Produit 5",
"price": 500,
"quantity": 5,
"checked": true
},
{
"id": "6",
"name": "Produit 6",
"price": 600,
"quantity": 5,
"checked": false
},
{
"id": "7",
"name": "Produit 7",
"price": 700,
"quantity": 5,
"checked": true
},
{
"id": "8",
"name": "Produit 8",
"price": 800,
"quantity": 5,
"checked": false
},
{
"id": "9",
"name": "Produit 9",
"price": 900,
"quantity": 5,
"checked": true
},
{
"id": "10",
"name": "Produit 10",
"price": 1000,
"quantity": 5,
"checked": false
},
{
"id": "aa4a",
"name": "tete",
"price": "478",
"quantity": 0,
"checked": true
},
{
"id": "d412",
"name": "gg",
"price": "15",
"quantity": 0,
"checked": true
},
{
"id": "9427",
"name": "gfdgfgf",
"price": "hgg",
"quantity": null,
"checked": true
}
]
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -0,0 +1,48 @@
.container {
max-width: 400px;
margin: 50px auto;
font-family: Arial, sans-serif;
}
form div {
margin-bottom: 15px;
}
label {
display: block;
font-weight: bold;
}
input {
width: 100%;
padding: 8px;
margin-top: 5px;
box-sizing: border-box;
}
span {
color: red;
font-size: 12px;
}
button {
padding: 10px 15px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
}
button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.info-box {
margin-top: 20px;
padding: 15px;
border: 1px solid #007bff;
background-color: #f9f9f9;
border-radius: 5px;
}

View file

@ -0,0 +1,48 @@
<div class="container">
<h1>Formulaire Réactif</h1>
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
<div>
<label for="nom">Nom :</label>
<input id="nom" formControlName="nom" />
<span *ngIf="userForm.get('nom')?.invalid && userForm.get('nom')?.touched">
Nom requis (minimum 2 caractères).
</span>
</div>
<div>
<label for="prenom">Prénom :</label>
<input id="prenom" formControlName="prenom" />
<span *ngIf="userForm.get('prenom')?.invalid && userForm.get('prenom')?.touched">
Prénom requis (minimum 2 caractères).
</span>
</div>
<div>
<label for="adresse">Adresse :</label>
<input id="adresse" formControlName="adresse" />
<span *ngIf="userForm.get('adresse')?.invalid && userForm.get('adresse')?.touched">
Adresse requise.
</span>
</div>
<div>
<label for="email">Email :</label>
<input id="email" formControlName="email" />
<span *ngIf="userForm.get('email')?.invalid && userForm.get('email')?.touched">
Email valide requis.
</span>
</div>
<button type="submit" [disabled]="userForm.invalid">Identifier</button>
</form>
<!-- Div cachée pour afficher les informations -->
<div *ngIf="showInfo" class="info-box">
<h2>Informations saisies :</h2>
<p><strong>Nom :</strong> {{ userInfo?.nom }}</p>
<p><strong>Prénom :</strong> {{ userInfo?.prenom }}</p>
<p><strong>Adresse :</strong> {{ userInfo?.adresse }}</p>
<p><strong>Email :</strong> {{ userInfo?.email }}</p>
</div>
</div>

View file

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

View file

@ -0,0 +1,33 @@
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-about',
templateUrl: './about.component.html',
styleUrl: './about.component.css'
})
export class AboutComponent {
userForm: FormGroup;
showInfo: boolean = false; // Pour afficher ou cacher la div
userInfo: any = null; // Stockage des informations saisies
constructor(private fb: FormBuilder) {
// Initialiser le formulaire
this.userForm = this.fb.group({
nom: ['', [Validators.required, Validators.minLength(2)]],
prenom: ['', [Validators.required, Validators.minLength(2)]],
adresse: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
});
}
// Méthode pour afficher les données
onSubmit() {
if (this.userForm.valid) {
this.userInfo = this.userForm.value; // Stocker les informations saisies
this.showInfo = true; // Afficher la div
} else {
alert('Veuillez corriger les erreurs dans le formulaire.');
}
}
}

View file

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

View file

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

View file

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-affproduit',
templateUrl: './affproduit.component.html',
styleUrl: './affproduit.component.css'
})
export class AffproduitComponent {
}

View file

@ -0,0 +1,28 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AboutComponent } from './about/about.component';
import { ContactsComponent } from './contacts/contacts.component';
import { UserComponent } from './user/user.component';
import { ProductComponent } from './product/product.component';
import { ProductList2Component } from './product-list2/product-list2.component';
import { SimpleproduitComponent } from './simpleproduit/simpleproduit.component';
const routes: Routes = [
{ path: 'about', component: AboutComponent },
{ path: 'contacts', component: ContactsComponent },
{ path: 'user', component: UserComponent },
{ path: 'product', component: ProductComponent },
{ path: 'product2', component: ProductList2Component },
{ path: 'gestprod', component: SimpleproduitComponent },
{
path: '',
redirectTo: '/about',
pathMatch: 'full'
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View file

View file

@ -0,0 +1,30 @@
<nav class="p-3">
<ul class="nav nav-pills">
<li>
<button routerLink="/about" class="btn btn-outline-success ms-1">A propos<i class="bi bi-house text-success"></i></button>
</li>
<li>
<button routerLink="/contacts" class="btn btn-outline-success ms-1">Contacts<i class="bi bi-arrow-down-up text-success"></i></button>
</li>
<li>
<button routerLink="/user" class="btn btn-outline-success ms-1">Usert<i class="bi bi-plus-circle text-success"></i></button>
</li>
<li>
<button routerLink="/product" class="btn btn-outline-success ms-1">Produit<i class="bi bi-plus-circle text-success"></i></button>
</li>
<!--<li>
<button routerLink="/product2" class="btn btn-outline-success ms-1">Produit2<i class="bi bi-plus-circle text-success"></i></button>
</li> -->
<li>
<button routerLink="/product2" class="btn btn-outline-success ms-1">Produit Observable<i class="bi bi-plus-circle text-success"></i></button>
</li>
<i class="bi bi-airplane" class="btn btn-outline-success ms-1" routerLink="/product2" ></i>
<li>
<button routerLink="/gestprod" class="btn btn-outline-success ms-1">Gestion Produit<i class="bi bi-app-indicator text-success"></i></button>
</li>
</ul>
</nav>
<router-outlet></router-outlet>

View file

@ -0,0 +1,35 @@
import { TestBed } from '@angular/core/testing';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterModule.forRoot([])
],
declarations: [
AppComponent
],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'second-app'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('second-app');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, second-app');
});
});

10
src/app/app.component.ts Normal file
View file

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
title = 'second-app';
}

43
src/app/app.module.ts Normal file
View file

@ -0,0 +1,43 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { AboutComponent } from './about/about.component';
import { ContactsComponent } from './contacts/contacts.component';
import { ProductComponent } from './product/product.component';
import { UserComponent } from './user/user.component';
import { ProductFormComponent } from './product/product-form/product-form.component';
import { ProductListComponent } from './product/product-list/product-list.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { provideHttpClient } from '@angular/common/http';
import { ProductList2Component } from './product-list2/product-list2.component';
import { SimpleproduitComponent } from './simpleproduit/simpleproduit.component';
import { CategorieComponent } from './categorie/categorie.component';
import { AffproduitComponent } from './affproduit/affproduit.component';
@NgModule({
declarations: [
AppComponent,
AboutComponent,
ContactsComponent,
ProductComponent,
UserComponent,
ProductFormComponent,
ProductListComponent,
ProductList2Component,
SimpleproduitComponent,
CategorieComponent,
AffproduitComponent
],
imports: [
BrowserModule,
AppRoutingModule,
ReactiveFormsModule,
FormsModule
],
providers: [provideHttpClient()],
bootstrap: [AppComponent]
})
export class AppModule { }

View file

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

View file

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

View file

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-categorie',
templateUrl: './categorie.component.html',
styleUrl: './categorie.component.css'
})
export class CategorieComponent {
}

View file

View file

@ -0,0 +1,2 @@
<h3>Liste de contacts</h3>
<button type="button" class="btn btn-secondary" (click)="onClick()">Retour About</button>

View file

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

View file

@ -0,0 +1,21 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-contacts',
templateUrl: './contacts.component.html',
styleUrl: './contacts.component.css'
})
export class ContactsComponent implements OnInit {
constructor(private route: Router){}
ngOnInit(): void {
}
onClick() {
this.route.navigate(['about']);
}
}

View file

@ -0,0 +1,7 @@
export interface Product {
id : number,
name : string,
price : number,
quantity : number,
checked : boolean
}

View file

@ -0,0 +1 @@
<p *ngFor="let product of listesProduits">{{product.name}} : {{product.price}}</p>

View file

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

View file

@ -0,0 +1,48 @@
import { Component, OnInit } from '@angular/core';
import { Product } from '../model/product.model';
import { ProductService } from '../services/product.service';
@Component({
selector: 'app-product-list2',
templateUrl: './product-list2.component.html',
styleUrl: './product-list2.component.css'
})
export class ProductList2Component implements OnInit {
listesProduits: Product[] = [];
produitSelectionne: Product | null = null;
constructor(private productService:ProductService){}
ngOnInit(): void {
//Méthode 1
// this.getProducts();
//Méthode 2
/*this.productService.getProduits2().subscribe(data => {
this.listesProduits = data;
});*/
//Methode 3
this.productService.getProduits2().subscribe({
next: (productsList) => {
this.listesProduits = productsList;
},
error: (error) => {
console.log('http error: ', error)
},
complete:() => {
console.log('Traitement finie')
}
},
);
}
getProducts(){
this.listesProduits = this.productService.getProduits();
}
}

View file

@ -0,0 +1,35 @@
<div class="p-3">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-md-6">
<form [formGroup]="productForm" (ngSubmit)="onSubmit()" >
<div class="mb-3">
<label class="form-label">Name</label>
<input class="form-control" formControlName="name">
<span *ngIf="productForm.get('name')?.invalid && productForm.get('name')?.touched">
<span id="message-error" class="text-danger"> Le nom du produit est obligatoire !</span>
</span>
</div>
<div class="mb-3">
<label class="form-label">Price</label>
<input class="form-control" formControlName="price">
<span *ngIf="productForm.get('price')?.invalid && productForm.get('price')?.touched">
<span id="message-error" class="text-danger"> Le prix du produit est obligatoire !</span>
</span>
</div>
<div class="mb-3">
<label class="form-label">Quantity</label>
<input class="form-control" formControlName="price">
</div>
<div class="mb-3">
<label class="form-label">Checked</label>
<input type="checkbox" class="form-check-input" formControlName="checked">
</div>
<button [disabled]="productForm.invalid" class="btn btn-success">Save</button>
</form>
</div>
</div>
</div>
</div>
</div>

View file

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

View file

@ -0,0 +1,58 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { Product } from '../../model/product.model';
@Component({
selector: 'app-product-form',
templateUrl: './product-form.component.html',
styleUrl: './product-form.component.css'
})
export class ProductFormComponent implements OnInit{
public productForm!:FormGroup;
@Input()
product: Product | null = null;
// <app-product-form [product]="valeurProvenantDuParent de type Product" (productPopulated)="Attend une fonction"></app-product-form>
/*@Output()
productPopulated: EventEmitter<Product> = new EventEmitter<Product>();*/
@Output() addProduit = new EventEmitter<Product>(); //@output permet de renvoyer les données du formulaire vers le composant parent
// Le composant parent gère la logique de l'application : il récupère les produits du service, les passe aux composants enfants, et réagit aux événements émis par ces derniers.
constructor(private fb: FormBuilder, private router: Router) {}
ngOnInit(): void {
//Peut être initialisé dans le constructeur
this.productForm=this.fb.group({
name : this.fb.control('', [Validators.required]),
price : this.fb.control('',[Validators.required, Validators.min(5)]),
quantity : this.fb.control(0,),
checked : this.fb.control(false),
});
if (this.product) {
this.productForm.patchValue(this.product);
}
}
/*onSubmit(): void {
if (this.productForm.valid) {
this.productPopulated.emit(this.productForm.value);
}
}*/
onSubmit() {
if (this.productForm.valid) {
this.addProduit.emit(this.productForm.value);
this.productForm.reset();
}
}
saveProduct(): void {
console.log(this.productForm.value);
}
}

View file

@ -0,0 +1,46 @@
<div class="p-3">
<div class="card">
<div class="card-body">
<div class="card-body">
<div>
<input class="p-1" type="text" [(ngModel)]="searchValue" placeholder="Rechercher un produit par nom..." >
<button (click)="onSearch()"
class="btn btn-outline-success ms-1">
<i class="bi bi-search"></i>
</button>
</div>
</div>
<!-- <p *ngFor="let user of myUsers">{{user.name}} : {{user.age}}</p>
<pre>{{users.length}}</pre>users!!!
<pre>{{products.length}}</pre>products!!! -->
<table class="table">
<thead>
<tr>
<th>Name</th> <th>Price</th> <th>Quantity</th> <th>Checked</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let product of products ">
<td>{{product.name}}</td>
<td>{{product.price}}</td>
<td>{{product.quantity}}</td>
<td>
<button (click)="onToggleDisponibilite(product)"
class="btn btn-outline-success">
<i [class]="product.checked?'bi bi-check':'bi bi-circle'"></i>
</button>
</td>
<td>
<button (click)="onDelete(product)" class="btn btn-outline-danger">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
<tr *ngIf="products.length === 0 && searchValue.length > 0">
<p>Aucun produit trouvé pour "{{ searchValue }}"</p>
</tr>
</tbody>
</table>
</div>
</div>
</div>

View file

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

View file

@ -0,0 +1,40 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Product } from '../../model/product.model';
import { ProductService } from '../../services/product.service';
@Component({
selector: 'app-product-list',
templateUrl: './product-list.component.html',
styleUrl: './product-list.component.css'
})
export class ProductListComponent implements OnInit {
//Le composant list-produit reçoit une liste de produits via @Input et émet des événements
// lorsqu'un produit doit être modifié ou supprimé via @Output
@Input() products: Product[] = [];
@Output() deleteProduit = new EventEmitter<Product>();
@Output() toggleDisponibilite = new EventEmitter<Product>();
@Output() searchProduct = new EventEmitter<string>()
searchValue: string = '';
constructor() {}
onDelete(product: Product) {
this.deleteProduit.emit(product);
}
onToggleDisponibilite(product: Product) {
this.toggleDisponibilite.emit(product);
}
onSearch() {
this.searchProduct.emit(this.searchValue);
}
ngOnInit(): void {}
}

View file

View file

@ -0,0 +1,11 @@
<app-product-form
[product]="produitSelectionne"
(addProduit)="onAddProduit($event)"
></app-product-form>
<app-product-list
[products]="listesProduits"
(deleteProduit)="onDeleteProduit($event)"
(toggleDisponibilite)="onToggleDisponibilite($event)"
(searchProduct)="onSearchProduit($event)"
>
</app-product-list>

View file

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

View file

@ -0,0 +1,91 @@
import { Component, OnInit } from '@angular/core';
import { Product } from '../model/product.model';
import { ProductService } from '../services/product.service';
@Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrl: './product.component.css'
})
export class ProductComponent implements OnInit {
//Le composant parent gère la logique de l'application : il récupère les produits du service, les passe aux composants enfants,
// et réagit aux événements émis par ces derniers.
listesProduits: Product[] = [];
produitSelectionne: Product | null = null;
constructor(private productService:ProductService){}
ngOnInit(): void {
///this.getProducts();
this.productService.getProduits2().subscribe(data => {
this.listesProduits = data;
}
);
/*this.productService.getProduits2().subscribe({
next: (productsList) => {
this.listesProduits = productsList;
},
error: (error) => {
console.log('http error: ', error)
},
complete:() => {
console.log('Traitement finie')
}
},
);*/
/**/
//this.productService.recupProduits().subscribe(data => (this.listesProduits = data));
//this.loadProducts();
}
getProducts(){
this.listesProduits = this.productService.getProduits();
}
loadProducts(): void {
this.productService.recupProduits().subscribe(data => (this.listesProduits = data));
}
onAddProduit(produit: Product) {
this.productService.ajouterProduit(produit);
}
onAddProduitAutre(produit: Product): void {
this.productService.addProduct(produit).subscribe(() => {
// this.productAdded.emit();
});
}
onDeleteProduit(product: Product) {
this.productService.supprimerProduit(product.id);
}
onToggleDisponibilite(product: Product) {
this.productService.recupererProduitParId(product.id);
}
onSearchProduitInit(name: string) {
this.productService.searchProduit(name);
}
onSearchProduit(name: string) {
this.listesProduits = this.productService.rechercherProduit(name)
}
onSearchProduit1(name: string) {
this.productService.searchProduit(name);
}
}

View file

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

View file

@ -0,0 +1,9 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class MonServiceService {
constructor() { }
}

View file

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

View file

@ -0,0 +1,132 @@
import { Injectable } from '@angular/core';
import { Product } from '../model/product.model';
import { BehaviorSubject, Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class ProductService {
produits: Product[] = [
{ id: 1, name: 'Produit 1', price: 100, quantity: 5, checked: true },
{ id: 2, name: 'Produit 2', price: 200, quantity: 5, checked: false },
{ id: 3, name: 'Produit 3', price: 300, quantity: 5, checked: true },
{ id: 4, name: 'Produit 4', price: 400, quantity: 5, checked: false },
{ id: 5, name: 'Produit 5', price: 500, quantity: 5, checked: true },
{ id: 6, name: 'Produit 6', price: 600, quantity: 5, checked: false },
{ id: 7, name: 'Produit 7', price: 700, quantity: 5, checked: true },
{ id: 8, name: 'Produit 8', price: 800, quantity: 5, checked: false },
{ id: 9, name: 'Produit 9', price: 900, quantity: 5, checked: true },
{ id: 10, name: 'Produit 10', price: 1000, quantity: 5, checked: false },
];
private productUrl = 'http://localhost:3600/produits';
constructor(private http: HttpClient) {}
recupProduits(): Observable<Product[]> {
return this.http.get<Product[]>(this.productUrl);
}
addProduit(produit: Product): Observable<Product> {
return this.http.post<Product>(this.productUrl, produit);
}
addProduct(produit: Product): Observable<Product> {
return this.http.post<Product>(this.productUrl, produit);
}
updateProduct(produit: Product): Observable<Product> {
return this.http.put<Product>(`${this.productUrl}/${produit.id}`, produit);
}
private produitsSubject = new BehaviorSubject<Product[]>(this.produits);
//private produitsSubject = new BehaviorSubject<Product[]>(this.productUrl);
private productsSubject = new BehaviorSubject<Product[]>([]);
public products$ = this.productsSubject.asObservable();
private loadProducts(): void {
this.http.get<Product[]>(this.productUrl)
.subscribe((products) => this.productsSubject.next(products));
}
// Obtenir les produits
getProducts(): Observable<Product[]> {
return this.products$;
}
// Ajouter un produit
addProduct2(product: Product): void {
const current = this.productsSubject.getValue();
const updated = [...current, product];
this.productsSubject.next(updated);
}
// Mettre à jour un produit
updateProduct2(updatedProduct: Product): void {
const current = this.productsSubject.getValue();
const updated = current.map(p => p.id === updatedProduct.id ? updatedProduct : p);
this.productsSubject.next(updated);
}
// Récupérer tous les produits
getProduits(): Product[] {
return this.produits;
//return this.produitsSubject.asObservable();
}
getProduits2(): Observable<Product[]> {
return this.produitsSubject.asObservable();
}
// Ajouter un produit
ajouterProduit(produit: Product): void {
produit.id = this.produits.length + 1;
this.produits.push(produit);
this.produitsSubject.next(this.produits);
}
// Supprimer un produit par son id
supprimerProduit(id: number): void {
this.produits = this.produits.filter((produit) => produit.id !== id);
this.produitsSubject.next(this.produits);
}
// Rechercher un produit par nom
rechercherProduitParNom(nom: string): Product | undefined {
return this.produits.find((produit) => produit.name.toLowerCase() === nom.toLowerCase());
}
rechercherProduit(nom: string): Product[] {
return this.produits.filter(produit =>
produit.name.toLowerCase().includes(nom.toLowerCase())
);
}
searchProduit(name: string) {
const filteredProduits = this.produits.filter(p => p.name.toLowerCase().includes(name.toLowerCase()));
this.produitsSubject.next(filteredProduits);
}
updateProduit(produit: Product): void {
const index = this.produits.findIndex(p => p.id === produit.id);
if (index !== -1) {
this.produits[index] = produit;
}
}
// Récupérer un produit par son id
recupererProduitParId(id: number): Product | undefined {
//const filteredProduits = this.produits.filter(p => p.name.toLowerCase().includes(name.toLowerCase()));
return this.produits.find((produit) => produit.id === id);
//this.produitsSubject.next(filteredProduits);
}
searchProduitByName(name: string): Observable<Product[]> {
return this.http.get<Product[]>(`${this.productUrl}?name_like=${name}`);
}
}

View file

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

View file

@ -0,0 +1,34 @@
import { Injectable } from '@angular/core';
import { Product } from '../model/product.model';
import { BehaviorSubject, Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class Product2Service {
private productUrl = 'http://localhost:3000/products'; // URL de l'API ou de JSON Server
constructor(private http: HttpClient) {}
// Méthode pour récupérer tous les produits
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(this.productUrl);
}
// Méthode pour ajouter un produit
addProduct(product: Product): Observable<Product> {
return this.http.post<Product>(this.productUrl, product);
}
// Méthode pour mettre à jour un produit
updateProduct(product: Product): Observable<Product> {
return this.http.put<Product>(`${this.productUrl}/${product.id}`, product);
}
// Méthode pour supprimer un produit
deleteProduct(id: number): Observable<void> {
return this.http.delete<void>(`${this.productUrl}/${id}`);
}
}

View file

@ -0,0 +1,43 @@
<div class="p-3">
<div class="card">
<div class="card-body">
<div class="card-body">
<div>
<input class="p-1" type="text" [(ngModel)]="searchValue" placeholder="Rechercher un produit par nom..." >
<button (click)="onSearch()"
class="btn btn-outline-success ms-1">
<i class="bi bi-search"></i>
</button>
</div>
</div>
<table class="table">
<thead>
<tr>
<th>Name</th> <th>Price</th> <th>Quantity</th> <th>Checked</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let product of listesProduits ">
<td>{{product.name}}</td>
<td>{{product.price}}</td>
<td>{{product.quantity}}</td>
<td>
<button (click)="onToggleDisponibilite(product)"
class="btn btn-outline-success">
<i [class]="product.checked?'bi bi-check':'bi bi-circle'"></i>
</button>
</td>
<td>
<button (click)="onDeleteProduit(product)" class="btn btn-outline-danger">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
<tr *ngIf="listesProduits.length === 0 && searchValue.length > 0">
<p>Aucun produit trouvé pour "{{ searchValue }}"</p>
</tr>
</tbody>
</table>
</div>
</div>
</div>

View file

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

View file

@ -0,0 +1,51 @@
import { Component, OnInit } from '@angular/core';
import { Product } from '../model/product.model';
import { ProductService } from '../services/product.service';
@Component({
selector: 'app-simpleproduit',
templateUrl: './simpleproduit.component.html',
styleUrl: './simpleproduit.component.css'
})
export class SimpleproduitComponent implements OnInit {
searchValue: string = '';
listesProduits: Product[] = [];
produitSelectionne: Product | null = null;
constructor(private productService:ProductService){}
ngOnInit(): void {
this.getProducts()
}
getProducts(){
this.listesProduits = this.productService.getProduits();
}
onAddProduit(produit: Product) {
this.productService.ajouterProduit(produit);
}
onDeleteProduit(product: Product) {
this.productService.supprimerProduit(product.id);
}
onToggleDisponibilite(product: Product) {
this.productService.recupererProduitParId(product.id);
}
onSearchProduitInit(name: string) {
this.productService.searchProduit(name);
}
onSearch() {
this.listesProduits = this.productService.rechercherProduit(this.searchValue)
}
onSearchProduit1(name: string) {
this.productService.searchProduit(name);
}
}

View file

View file

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

View file

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

View file

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrl: './user.component.css'
})
export class UserComponent {
}

13
src/index.html Normal file
View file

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>SecondApp</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>

8
src/main.ts Normal file
View file

@ -0,0 +1,8 @@
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule, {
ngZoneEventCoalescing: true
})
.catch(err => console.error(err));

9
src/produit.json Normal file
View file

@ -0,0 +1,9 @@
{
"produits": [
{ "id": 1, "nom": "Produit 1", "quantite": 10, "disponibilite": true },
{ "id": 2, "nom": "Produit 2", "quantite": 15, "disponibilite": false },
{ "id": 3, "nom": "Produit 3", "quantite": 20, "disponibilite": true },
{ "id": 4, "nom": "Produit 4", "quantite": 5, "disponibilite": true },
{ "id": 5, "nom": "Produit 5", "quantite": 12, "disponibilite": false }
]
}

7
src/styles.css Normal file
View file

@ -0,0 +1,7 @@
/* You can add global styles to this file, and also import other style files */
/*@import 'bootstrap/dist/css/bootstrap.min.css';*/
@import "bootstrap-icons/font/bootstrap-icons.css";

15
tsconfig.app.json Normal file
View file

@ -0,0 +1,15 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts"
],
"include": [
"src/**/*.d.ts"
]
}

33
tsconfig.json Normal file
View file

@ -0,0 +1,33 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist/out-tsc",
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"isolatedModules": true,
"esModuleInterop": true,
"sourceMap": true,
"declaration": false,
"experimentalDecorators": true,
"moduleResolution": "bundler",
"importHelpers": true,
"target": "ES2022",
"module": "ES2022",
"lib": [
"ES2022",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}

15
tsconfig.spec.json Normal file
View file

@ -0,0 +1,15 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
]
},
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}