This commit is contained in:
ExostFlash 2025-07-11 17:13:18 +02:00
commit d270f2f1f5
59 changed files with 16099 additions and 0 deletions

17
.editorconfig Normal file
View file

@ -0,0 +1,17 @@
# 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
ij_typescript_use_double_quotes = false
[*.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

4
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,4 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": ["angular.ng-template"]
}

20
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,20 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
},
{
"name": "ng test",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: test",
"url": "http://localhost:9876/debug.html"
}
]
}

42
.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,42 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
},
{
"type": "npm",
"script": "test",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
}
]
}

59
README.md Normal file
View file

@ -0,0 +1,59 @@
# Project
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.0.
## Development server
To start a local development server, run:
```bash
ng serve
```
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
## Code scaffolding
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
```bash
ng generate component component-name
```
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
```bash
ng generate --help
```
## Building
To build the project run:
```bash
ng build
```
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
## Running unit tests
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
```bash
ng test
```
## Running end-to-end tests
For end-to-end (e2e) testing, run:
```bash
ng e2e
```
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
## Additional Resources
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.

108
angular.json Normal file
View file

@ -0,0 +1,108 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"project": {
"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/project",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"@angular/material/prebuilt-themes/cyan-orange.css",
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "project:build:production"
},
"development": {
"buildTarget": "project: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": [
"@angular/material/prebuilt-themes/cyan-orange.css",
"src/styles.css"
],
"scripts": []
}
}
}
}
}
}

14881
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

39
package.json Normal file
View file

@ -0,0 +1,39 @@
{
"name": "project",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/cdk": "^19.2.19",
"@angular/common": "^19.2.0",
"@angular/compiler": "^19.2.0",
"@angular/core": "^19.2.0",
"@angular/forms": "^19.2.0",
"@angular/material": "^19.2.19",
"@angular/platform-browser": "^19.2.0",
"@angular/platform-browser-dynamic": "^19.2.0",
"@angular/router": "^19.2.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^19.2.0",
"@angular/cli": "^19.2.0",
"@angular/compiler-cli": "^19.2.0",
"@types/jasmine": "~5.1.0",
"jasmine-core": "~5.6.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.7.2"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -0,0 +1,29 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { UserListComponent } from './components/users/user-list/user-list.component';
import { CoursListComponent } from './components/cours/cours-list/cours-list.component';
import { CoursDetailComponent } from './components/cours/cours-detail/cours-detail.component';
import { CoursFormComponent } from './components/cours/cours-form/cours-form.component';
import { GroupFormComponent } from './components/groups/group-form/group-form.component';
import { GroupDetailComponent } from './components/groups/group-detail/group-detail.component';
import { GroupListComponent } from './components/groups/group-list/group-list.component';
const routes: Routes = [
{ path: 'cours/create', component: CoursFormComponent },
{ path: 'cours/:id/edit', component: CoursFormComponent },
{ path: 'cours/:id', component: CoursDetailComponent },
{ path: 'cours', component: CoursListComponent },
{ path: 'users', component: UserListComponent },
{ path: 'groups/create', component: GroupFormComponent },
{ path: 'groups/:id', component: GroupDetailComponent },
{ path: 'groups', component: GroupListComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View file

View file

@ -0,0 +1,3 @@
<div class="container mt-4">
<router-outlet></router-outlet>
</div>

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 'project'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('project');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, project');
});
});

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

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

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

@ -0,0 +1,40 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { UserListComponent } from './components/users/user-list/user-list.component';
import { CoursListComponent } from './components/cours/cours-list/cours-list.component';
import { CoursDetailComponent } from './components/cours/cours-detail/cours-detail.component';
import { CoursFormComponent } from './components/cours/cours-form/cours-form.component';
import { GroupFormComponent } from './components/groups/group-form/group-form.component';
import { GroupDetailComponent } from './components/groups/group-detail/group-detail.component';
import { GroupListComponent } from './components/groups/group-list/group-list.component';
@NgModule({
declarations: [
AppComponent,
UserListComponent,
CoursListComponent,
CoursDetailComponent,
CoursFormComponent,
GroupFormComponent,
GroupDetailComponent,
GroupListComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
ReactiveFormsModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

View file

@ -0,0 +1,6 @@
<div *ngIf="cours">
<h2>{{ cours.name }}</h2>
<p>{{ cours.description }}</p>
<a [routerLink]="['/cours', cours.id, 'edit']">Modifier</a>
<a routerLink="/cours">Retour à la liste</a>
</div>

View file

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

View file

@ -0,0 +1,25 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { CoursService } from '../../../services/cours.service';
import { Cours } from '../../../models/cours';
@Component({
selector: 'app-cours-detail',
standalone: false,
templateUrl: './cours-detail.component.html',
styleUrl: './cours-detail.component.css'
})
export class CoursDetailComponent implements OnInit {
cours?: Cours;
constructor(
private route: ActivatedRoute,
private coursService: CoursService
) {}
ngOnInit(): void {
const id = Number(this.route.snapshot.paramMap.get('id'));
this.coursService.getById(id).subscribe(data => this.cours = data);
}
}

View file

@ -0,0 +1,9 @@
<h2>{{ isEdit ? 'Modifier le cours' : 'Ajouter un cours' }}</h2>
<form [formGroup]="coursForm" (ngSubmit)="onSubmit()">
<label>Nom :</label>
<input formControlName="name" required>
<label>Description :</label>
<textarea formControlName="description" required></textarea>
<button type="submit">{{ isEdit ? 'Mettre à jour' : 'Ajouter' }}</button>
<a routerLink="/cours">Annuler</a>
</form>

View file

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

View file

@ -0,0 +1,55 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { CoursService } from '../../../services/cours.service';
import { Cours } from '../../../models/cours';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-cours-form',
standalone: false,
templateUrl: './cours-form.component.html',
styleUrl: './cours-form.component.css'
})
export class CoursFormComponent implements OnInit {
coursForm: FormGroup;
isEdit = false;
coursId?: number;
constructor(
private fb: FormBuilder,
private coursService: CoursService,
private route: ActivatedRoute,
private router: Router
) {
this.coursForm = this.fb.group({
name: ['', Validators.required],
description: ['', Validators.required]
});
}
ngOnInit(): void {
this.coursId = Number(this.route.snapshot.paramMap.get('id'));
this.isEdit = !!this.coursId;
if (this.isEdit) {
this.coursService.getById(this.coursId).subscribe(cours => {
this.coursForm.patchValue(cours);
});
}
}
onSubmit() {
if (this.coursForm.valid) {
if (this.isEdit) {
this.coursService.update(this.coursId!, this.coursForm.value).subscribe(() => {
this.router.navigate(['/cours', this.coursId]);
});
} else {
this.coursService.create(this.coursForm.value).subscribe(() => {
this.router.navigate(['/cours']);
});
}
}
}
}

View file

@ -0,0 +1,9 @@
<h2>Liste des cours</h2>
<a routerLink="/cours/new">Ajouter un cours</a>
<ul>
<li *ngFor="let cours of coursList">
<a [routerLink]="['/cours', cours.id]">{{ cours.name }}</a>
<button [routerLink]="['/cours', cours.id, 'edit']">Modifier</button>
<button (click)="deleteCours(cours.id)">Supprimer</button>
</li>
</ul>

View file

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

View file

@ -0,0 +1,26 @@
import { Component, OnInit } from '@angular/core';
import { CoursService } from '../../../services/cours.service';
import { Cours } from '../../../models/cours';
@Component({
selector: 'app-cours-list',
standalone: false,
templateUrl: './cours-list.component.html',
styleUrl: './cours-list.component.css'
})
export class CoursListComponent implements OnInit {
coursList: Cours[] = [];
constructor(private coursService: CoursService) {}
ngOnInit(): void {
this.coursService.getAll().subscribe(data => this.coursList = data);
}
deleteCours(id: number) {
this.coursService.delete(id).subscribe(() => {
this.coursList = this.coursList.filter(c => c.id !== id);
});
}
}

View file

@ -0,0 +1,24 @@
<div *ngIf="group">
<h2>{{ group.name }}</h2>
<p>{{ group.description }}</p>
<h3>Membres</h3>
<ul>
<li *ngFor="let user of group.members">
{{ getUserName(user) }}
<button (click)="removeUser(user)">Supprimer</button>
</li>
</ul>
<input type="number" [(ngModel)]="userIdToAdd" placeholder="ID utilisateur">
<button (click)="addUser()">Ajouter utilisateur</button>
<h3>Cours</h3>
<ul>
<li *ngFor="let cours of group.cours">
{{ getCoursName(cours) }}
<button (click)="removeCours(cours)">Supprimer</button>
</li>
</ul>
<input type="number" [(ngModel)]="coursIdToAdd" placeholder="ID cours">
<button (click)="addCours()">Ajouter cours</button>
</div>

View file

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

View file

@ -0,0 +1,83 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { GroupService } from '../../../services/group.service';
import { Group } from '../../../models/group';
import { UserService } from '../../../services/user.service';
import { User } from '../../../models/user';
import { CoursService } from '../../../services/cours.service';
import { Cours } from '../../../models/cours';
@Component({
selector: 'app-group-detail',
standalone: false,
templateUrl: './group-detail.component.html',
styleUrl: './group-detail.component.css'
})
export class GroupDetailComponent implements OnInit {
group?: Group;
userIdToAdd: number = 0;
coursIdToAdd: number = 0;
users: User[] = [];
coursList: Cours[] = [];
constructor(
private route: ActivatedRoute,
private groupService: GroupService,
private userService: UserService,
private coursService: CoursService
) {}
ngOnInit(): void {
const id = Number(this.route.snapshot.paramMap.get('id'));
this.groupService.getById(id).subscribe(data => this.group = data);
this.userService.getAll().subscribe(data => this.users = data);
this.coursService.getAll().subscribe(data => this.coursList = data);
}
getUserName(userId: number) {
const user = this.users.find(u => u.id === userId);
return user ? user.first_name + ' ' + user.last_name : userId;
}
getCoursName(coursId: number) {
const cours = this.coursList.find(c => c.id === coursId);
return cours ? cours.name : coursId;
}
addUser() {
if (this.group && this.userIdToAdd) {
this.groupService.addUserToGroup(this.group.id, this.userIdToAdd).subscribe(() => {
this.ngOnInit();
this.userIdToAdd = 0;
});
}
}
removeUser(userId: number) {
if (this.group) {
this.groupService.removeUserFromGroup(this.group.id, userId).subscribe(() => {
this.ngOnInit();
});
}
}
addCours() {
if (this.group && this.coursIdToAdd) {
this.groupService.addCoursToGroup(this.group.id, this.coursIdToAdd).subscribe(() => {
this.ngOnInit();
this.coursIdToAdd = 0;
});
}
}
removeCours(coursId: number) {
if (this.group) {
this.groupService.removeCoursFromGroup(this.group.id, coursId).subscribe(() => {
this.ngOnInit();
});
}
}
}

View file

@ -0,0 +1,9 @@
<h2>Créer un groupe</h2>
<form [formGroup]="groupForm" (ngSubmit)="onSubmit()">
<label>Nom :</label>
<input formControlName="name" required>
<label>Description :</label>
<textarea formControlName="description" required></textarea>
<button type="submit">Créer</button>
<a routerLink="/groups">Annuler</a>
</form>

View file

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GroupFormComponent } from './group-form.component';
describe('GroupFormComponent', () => {
let component: GroupFormComponent;
let fixture: ComponentFixture<GroupFormComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [GroupFormComponent]
})
.compileComponents();
fixture = TestBed.createComponent(GroupFormComponent);
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';
import { GroupService } from '../../../services/group.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-group-form',
standalone: false,
templateUrl: './group-form.component.html',
styleUrl: './group-form.component.css'
})
export class GroupFormComponent {
groupForm: FormGroup;
constructor(
private fb: FormBuilder,
private groupService: GroupService,
private router: Router
) {
this.groupForm = this.fb.group({
name: ['', Validators.required],
description: ['', Validators.required]
});
}
onSubmit() {
if (this.groupForm.valid) {
this.groupService.create(this.groupForm.value).subscribe(() => {
this.router.navigate(['/groups']);
});
}
}
}

View file

@ -0,0 +1,8 @@
<h2>Liste des groupes</h2>
<a routerLink="/groups/create">Créer un groupe</a>
<ul>
<li *ngFor="let group of groups">
<a [routerLink]="['/groups', group.id]">{{ group.name }}</a>
<button (click)="deleteGroup(group.id)">Supprimer</button>
</li>
</ul>

View file

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

View file

@ -0,0 +1,25 @@
import { Component, OnInit } from '@angular/core';
import { GroupService } from '../../../services/group.service';
import { Group } from '../../../models/group';
@Component({
selector: 'app-group-list',
standalone: false,
templateUrl: './group-list.component.html',
styleUrl: './group-list.component.css'
})
export class GroupListComponent implements OnInit {
groups: Group[] = [];
constructor(private groupService: GroupService) {}
ngOnInit(): void {
this.groupService.getAll().subscribe(data => this.groups = data);
}
deleteGroup(id: number) {
this.groupService.delete(id).subscribe(() => {
this.groups = this.groups.filter(g => g.id !== id);
});
}
}

View file

@ -0,0 +1,6 @@
<h2>Liste des utilisateurs</h2>
<ul>
<li *ngFor="let user of users">
{{ user.first_name }} {{ user.last_name }} - {{ user.role }}
</li>
</ul>

View file

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

View file

@ -0,0 +1,22 @@
import { Component, OnInit } from '@angular/core';
import { UserService } from './../../../services/user.service';
import { User } from './../../../models/user';
@Component({
selector: 'app-user-list',
standalone: false,
templateUrl: './user-list.component.html',
styleUrl: './user-list.component.css'
})
export class UserListComponent implements OnInit {
users: User[] = [];
constructor(private userService: UserService) {}
ngOnInit(): void {
this.userService.getAll().subscribe(data => this.users = data);
}
}

5
src/app/models/cours.ts Normal file
View file

@ -0,0 +1,5 @@
export interface Cours {
id: number;
name: string;
description: string;
}

7
src/app/models/group.ts Normal file
View file

@ -0,0 +1,7 @@
export interface Group {
id: number;
name: string;
description: string;
members: number[];
cours: number[];
}

7
src/app/models/user.ts Normal file
View file

@ -0,0 +1,7 @@
export interface User {
id: number;
first_name: string;
last_name: string;
email: string;
role: 'admin' | 'student';
}

View file

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

View file

@ -0,0 +1,34 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Cours } from '../models/cours';
@Injectable({
providedIn: 'root'
})
export class CoursService {
private apiUrl = 'http://localhost:3000/cours';
constructor(private http: HttpClient) {}
getAll(): Observable<Cours[]> {
return this.http.get<Cours[]>(this.apiUrl);
}
getById(id: number): Observable<Cours> {
return this.http.get<Cours>(`${this.apiUrl}/${id}`);
}
create(cours: Cours): Observable<any> {
return this.http.post(this.apiUrl, cours);
}
update(id: number, cours: Cours): Observable<any> {
return this.http.put(`${this.apiUrl}/${id}`, cours);
}
delete(id: number): Observable<any> {
return this.http.delete(`${this.apiUrl}/${id}`);
}
}

View file

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

View file

@ -0,0 +1,50 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Group } from '../models/group';
@Injectable({
providedIn: 'root'
})
export class GroupService {
private apiUrl = 'http://localhost:3000/groups';
constructor(private http: HttpClient) {}
getAll(): Observable<Group[]> {
return this.http.get<Group[]>(this.apiUrl);
}
getById(id: number): Observable<Group> {
return this.http.get<Group>(`${this.apiUrl}/${id}`);
}
create(group: Partial<Group>): Observable<any> {
return this.http.post(this.apiUrl, group);
}
update(id: number, group: Partial<Group>): Observable<any> {
return this.http.put(`${this.apiUrl}/${id}`, group);
}
delete(id: number): Observable<any> {
return this.http.delete(`${this.apiUrl}/${id}`);
}
addUserToGroup(groupId: number, userId: number): Observable<any> {
return this.http.post(`${this.apiUrl}/${groupId}/users`, { userId });
}
removeUserFromGroup(groupId: number, userId: number): Observable<any> {
return this.http.delete(`${this.apiUrl}/${groupId}/users/${userId}`);
}
addCoursToGroup(groupId: number, coursId: number): Observable<any> {
return this.http.post(`${this.apiUrl}/${groupId}/cours`, { coursId: coursId });
}
removeCoursFromGroup(groupId: number, coursId: number): Observable<any> {
return this.http.delete(`${this.apiUrl}/${groupId}/cours/${coursId}`);
}
}

View file

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

View file

@ -0,0 +1,34 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from './../models/user';
@Injectable({
providedIn: 'root'
})
export class UserService {
private apiUrl = 'http://localhost:3000/users';
constructor(private http: HttpClient) {}
getAll(): Observable<User[]> {
return this.http.get<User[]>(this.apiUrl);
}
getById(id: number): Observable<User> {
return this.http.get<User>(`${this.apiUrl}/${id}`);
}
create(user: User): Observable<any> {
return this.http.post(this.apiUrl, user);
}
update(id: number, user: User): Observable<any> {
return this.http.put(`${this.apiUrl}/${id}`, user);
}
delete(id: number): Observable<any> {
return this.http.delete(`${this.apiUrl}/${id}`);
}
}

15
src/index.html Normal file
View file

@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Project</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body class="mat-typography">
<app-root></app-root>
</body>
</html>

7
src/main.ts Normal file
View file

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

4
src/styles.css Normal file
View file

@ -0,0 +1,4 @@
/* You can add global styles to this file, and also import other style files */
html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }

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"
]
}

27
tsconfig.json Normal file
View file

@ -0,0 +1,27 @@
/* 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,
"experimentalDecorators": true,
"moduleResolution": "bundler",
"importHelpers": true,
"target": "ES2022",
"module": "ES2022"
},
"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"
]
}