From 7dfd83b60ad2c51a59c4809cfdb3491d8464b96a Mon Sep 17 00:00:00 2001 From: Bastien OLLIER Date: Mon, 3 Jun 2024 15:01:55 +0200 Subject: [PATCH 1/6] Implement personal code rooms (#11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: bastien ollier Reviewed-on: https://codefirst.iut.uca.fr/git/sandkasten/sandkasten-web/pulls/11 Reviewed-by: Clément FRÉVILLE Co-authored-by: Bastien OLLIER Co-committed-by: Bastien OLLIER --- .gitignore | 1 + package-lock.json | 10 ++ package.json | 1 + .../{app-routing.module.ts => app.routes.ts} | 12 +- .../components/editor/editor.component.html | 8 +- .../components/editor/editor.component.scss | 10 ++ src/app/components/editor/editor.component.ts | 45 ++++++- ...n.service.ts => backendService.service.ts} | 11 +- src/app/services/connection.service.ts | 120 ++++++++++++++++++ src/main.ts | 5 +- 10 files changed, 203 insertions(+), 20 deletions(-) rename src/app/{app-routing.module.ts => app.routes.ts} (81%) rename src/app/services/{codeExecution.service.ts => backendService.service.ts} (80%) create mode 100644 src/app/services/connection.service.ts diff --git a/.gitignore b/.gitignore index a368413..1f86315 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ yarn-error.log *.launch .settings/ *.sublime-workspace +.nx # Visual Studio Code .vscode/* diff --git a/package-lock.json b/package-lock.json index f140234..b4f06de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@angular/platform-browser": "^17.3.7", "@angular/platform-browser-dynamic": "^17.3.7", "@angular/router": "^17.3.7", + "@codemirror/collab": "^6.1.1", "@codemirror/lang-cpp": "^6.0.2", "@codemirror/lang-javascript": "^6.2.2", "@codemirror/state": "^6.4.1", @@ -2447,6 +2448,15 @@ "@lezer/common": "^1.0.0" } }, + "node_modules/@codemirror/collab": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@codemirror/collab/-/collab-6.1.1.tgz", + "integrity": "sha512-tkIn9Jguh98ie12dbBuba3lE8LHUkaMrIFuCVeVGhncSczFdKmX25vC12+58+yqQW5AXi3py6jWY0W+jelyglA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0" + } + }, "node_modules/@codemirror/commands": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.5.0.tgz", diff --git a/package.json b/package.json index afaa066..d23a824 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@angular/platform-browser": "^17.3.7", "@angular/platform-browser-dynamic": "^17.3.7", "@angular/router": "^17.3.7", + "@codemirror/collab": "^6.1.1", "@codemirror/lang-cpp": "^6.0.2", "@codemirror/lang-javascript": "^6.2.2", "@codemirror/state": "^6.4.1", diff --git a/src/app/app-routing.module.ts b/src/app/app.routes.ts similarity index 81% rename from src/app/app-routing.module.ts rename to src/app/app.routes.ts index 2ec202c..29b9581 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app.routes.ts @@ -1,5 +1,4 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { Routes } from '@angular/router'; import { EditorComponent } from './components/editor/editor.component'; import { LandingPageComponent } from './components/landing-page/landing-page.component'; import { DocumentationComponent } from './components/documentation/documentation.component'; @@ -9,18 +8,13 @@ import { OurStoryComponent } from './components/our-story/our-story.component'; import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component'; // Toutes les routes de l'application sont définies ici -const routes: Routes = [ +export const routes: Routes = [ { path: '', component: LandingPageComponent }, { path: 'editor', component: EditorComponent }, + { path: 'editor-live/:idRoom', component: EditorComponent }, { path: 'documentation', component: DocumentationComponent }, { path: 'contact', component: FormComponent }, { path: 'our-story', component: OurStoryComponent }, { path: 'terms-of-service', component: TermsOfServiceComponent }, { path: 'privacy-policy', component: PrivacyPolicyComponent }, ]; - -@NgModule({ - imports: [RouterModule.forRoot(routes)], - exports: [RouterModule], -}) -export class AppRoutingModule {} diff --git a/src/app/components/editor/editor.component.html b/src/app/components/editor/editor.component.html index df54939..ad07817 100644 --- a/src/app/components/editor/editor.component.html +++ b/src/app/components/editor/editor.component.html @@ -59,6 +59,13 @@ } + + + + Login is required + + + + + Password + + + + Password is required + + + + + +
{{ errorLogin }}
+
{{ successLogin }}
+ diff --git a/src/app/components/login/login.component.spec.ts b/src/app/components/login/login.component.spec.ts new file mode 100644 index 0000000..1166f9c --- /dev/null +++ b/src/app/components/login/login.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginComponent } from './login.component'; +import { HttpClientModule } from '@angular/common/http'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LoginComponent, HttpClientModule, NoopAnimationsModule], + }).compileComponents(); + + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/login/login.component.ts b/src/app/components/login/login.component.ts new file mode 100644 index 0000000..51179e1 --- /dev/null +++ b/src/app/components/login/login.component.ts @@ -0,0 +1,71 @@ +import { Component } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + FormControl, + Validators, + FormsModule, + ReactiveFormsModule, + NgForm, + FormGroup, + FormBuilder, +} from '@angular/forms'; +import { MatInputModule } from '@angular/material/input'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { merge } from 'rxjs'; +import { MatIconModule } from '@angular/material/icon'; +import { MatButtonModule } from '@angular/material/button'; +import { CommonModule } from '@angular/common'; +import { UserService } from 'src/app/services/user.service'; +import { User } from 'src/app/models/user.model'; + +@Component({ + selector: 'app-auth', + templateUrl: './login.component.html', + styleUrl: './login.component.css', + standalone: true, + imports: [ + MatFormFieldModule, + MatInputModule, + FormsModule, + ReactiveFormsModule, + MatButtonModule, + MatIconModule, + CommonModule, + ], +}) +export class LoginComponent { + hide = true; + + loginForm = this.formBuilder.group({ + login: ['', Validators.required], + password: ['', Validators.required], + }); + + errorMessage = ''; + + successLogin = ''; + errorLogin = ''; + + constructor( + private userService: UserService, + private formBuilder: FormBuilder + ) { + } + + loginAction() { + const formValue = this.loginForm.value; + + this.userService + .loginUser(formValue.login!, formValue.password!) + .subscribe((response) => { + console.log('response :', response); + if (response.success) { + this.successLogin = 'Vous êtes connecté.'; + this.errorLogin = ''; + } else { + this.errorLogin = "L'authentification a échoué."; + this.successLogin = ''; + } + }); + } +} diff --git a/src/app/components/register/register.component.css b/src/app/components/register/register.component.css new file mode 100644 index 0000000..5eb5cdd --- /dev/null +++ b/src/app/components/register/register.component.css @@ -0,0 +1,18 @@ +.form-container { + display: flex; + flex-direction: column; +} + +.form-field { + width: 100%; +} + +.success-message { + color: green; + margin-top: 20px; +} + +.error-message { + color: red; + margin-top: 20px; +} diff --git a/src/app/components/register/register.component.html b/src/app/components/register/register.component.html new file mode 100644 index 0000000..1c3bf41 --- /dev/null +++ b/src/app/components/register/register.component.html @@ -0,0 +1,53 @@ +
+

Formulaire d'inscription :

+ + + Enter your email + + {{ + errorMessage + }} + + + + Enter your login + + {{ + errorMessage + }} + + + + Enter your password + + + {{ + errorMessage + }} + + + + + +
+ {{ successRegister }} +
+
+ {{ errorRegister }} +
+
diff --git a/src/app/components/register/register.component.ts b/src/app/components/register/register.component.ts new file mode 100644 index 0000000..d5467a4 --- /dev/null +++ b/src/app/components/register/register.component.ts @@ -0,0 +1,75 @@ +import { Component } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + FormControl, + Validators, + FormsModule, + ReactiveFormsModule, + NgForm, + FormBuilder, +} from '@angular/forms'; +import { MatInputModule } from '@angular/material/input'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { merge } from 'rxjs'; +import { MatIconModule } from '@angular/material/icon'; +import { MatButtonModule } from '@angular/material/button'; +import { CommonModule } from '@angular/common'; +import { UserService } from 'src/app/services/user.service'; +import { User } from 'src/app/models/user.model'; + +@Component({ + selector: 'app-auth', + templateUrl: './register.component.html', + styleUrl: './register.component.css', + standalone: true, + imports: [ + MatFormFieldModule, + MatInputModule, + FormsModule, + ReactiveFormsModule, + MatButtonModule, + MatIconModule, + CommonModule, + ], +}) +export class RegisterComponent { + hide = true; + + registerForm = this.formBuilder.group({ + email: ['', Validators.required], + login: ['', Validators.required], + password: ['', Validators.required], + }); + errorMessage = ''; + + successRegister = ''; + errorRegister = ''; + + constructor( + private userService: UserService, + private formBuilder: FormBuilder + ) { + } + + register() { + const formRegisterValue = this.registerForm.value; + + this.userService + .postUser( + formRegisterValue.email!, + formRegisterValue.login!, + formRegisterValue.password! + ) + .subscribe((response) => { + console.log('response :', response); + if (response.success) { + this.successRegister = 'Votre compte a été créé avec succès.'; + this.errorRegister = ''; + } else { + this.errorRegister = + "L'inscription a échoué : un compte avec ce login existe déjà."; + this.successRegister = ''; + } + }); + } +} diff --git a/src/app/models/user.model.ts b/src/app/models/user.model.ts new file mode 100644 index 0000000..55c3826 --- /dev/null +++ b/src/app/models/user.model.ts @@ -0,0 +1,6 @@ +export interface User { + id_user: number; + login: string; + password: string; + permissions: number; +} diff --git a/src/app/services/user.service.ts b/src/app/services/user.service.ts new file mode 100644 index 0000000..d2ca330 --- /dev/null +++ b/src/app/services/user.service.ts @@ -0,0 +1,50 @@ +import { Injectable } from '@angular/core'; +import { User } from '../models/user.model'; +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { NgForm } from '@angular/forms'; + +@Injectable({ + providedIn: 'root', +}) +export class UserService { + API_URL = 'http://127.0.0.1:3000'; + + constructor(private http: HttpClient) {} + + postUser( + email: string, + login: string, + password: string + ): Observable { + const body = { + email: email, + login: login, + password: password, + permissions: 0, + }; + + return this.http.post(`${this.API_URL}/users`, body); + } + + loginUser(login: string, password: string): Observable { + const body = { + login: login, + password: password, + }; + + return this.http.post(`${this.API_URL}/users/login`, body, { + withCredentials: true, + }); + } + + logoutUser(): Observable { + return this.http.post(`${this.API_URL}/users/logout`, { + withCredentials: true, + }); + } +} + +type Response = { + success: boolean; +}; diff --git a/src/index.html b/src/index.html index 4adf77b..464867f 100644 --- a/src/index.html +++ b/src/index.html @@ -11,8 +11,14 @@ + + - + diff --git a/src/main.ts b/src/main.ts index 504e152..713ebd7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,6 +10,7 @@ import { import { ReactiveFormsModule, FormsModule } from '@angular/forms'; import { BrowserModule, bootstrapApplication } from '@angular/platform-browser'; import { TranslationService } from './app/services/translation.service'; +import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; import { provideRouter, withComponentInputBinding } from '@angular/router'; import { routes } from './app/app.routes'; @@ -31,5 +32,6 @@ bootstrapApplication(AppComponent, { ), TranslationService, provideHttpClient(withInterceptorsFromDi()), + provideAnimationsAsync(), ], }).catch(console.error);