diff --git a/angular.json b/angular.json index 4bd67bb..50745c6 100644 --- a/angular.json +++ b/angular.json @@ -47,10 +47,11 @@ "src/favicon.ico" ], "styles": [ - "@angular/material/prebuilt-themes/deeppurple-amber.css", "src/styles.scss" ], - "scripts": [] + "scripts": [ + "node_modules/flowbite/dist/flowbite.min.js" + ] }, "configurations": { "production": { @@ -110,7 +111,6 @@ "src/favicon.ico" ], "styles": [ - "@angular/material/prebuilt-themes/deeppurple-amber.scss", "src/styles.scss" ], "scripts": [] diff --git a/package.json b/package.json index 442d257..98dc225 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,10 @@ "@angular/compiler": "^15.2.0", "@angular/core": "^15.2.0", "@angular/forms": "^15.2.0", - "@angular/material": "^15.2.9", "@angular/platform-browser": "^15.2.0", "@angular/platform-browser-dynamic": "^15.2.0", "@angular/router": "^15.2.0", + "flowbite": "^2.5.1", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.12.0" @@ -34,7 +34,7 @@ "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.0.0", - "tailwindcss": "^3.4.3", + "tailwindcss": "^3.4.10", "typescript": "~4.9.4" } } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index fe8da85..eef36c1 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,9 +1,18 @@ import { Component, OnInit } from '@angular/core'; +import { AuthService } from './core/services/auth.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) -export class AppComponent { +export class AppComponent implements OnInit { + constructor( + private authService: AuthService + ) { + } + + ngOnInit(): void { + this.authService.readUserState(); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index ad51b68..5c578f8 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -8,9 +8,11 @@ import { RouterModule, Routes } from '@angular/router'; import { SharedModule } from './shared/shared.module'; import { HomeComponent } from './core/components/home/home.component'; import { CoreModule } from './core/core.module'; +import { AuthGuard } from './core/guards/auth.guard'; const routes: Routes = [ - { path: 'home', component: HomeComponent }, + { path: 'auth', loadChildren: () => import('./core/auth/auth.module').then(m => m.AuthModule) }, + { path: 'home', component: HomeComponent, canActivate: [AuthGuard] }, { path: '', redirectTo: 'home', pathMatch: 'full' }, ]; diff --git a/src/app/core/auth/auth.module.ts b/src/app/core/auth/auth.module.ts new file mode 100644 index 0000000..82680d8 --- /dev/null +++ b/src/app/core/auth/auth.module.ts @@ -0,0 +1,33 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { LoginComponent } from './components/login/login.component'; +import { RegistrationComponent } from './components/registration/registration.component'; +import { ConfirmRegistrationComponent } from './components/confirm-registration/confirm-registration.component'; +import { RouterModule, Routes } from '@angular/router'; +import { ReactiveFormsModule } from '@angular/forms'; + +const routes: Routes = [ + { path: 'login', component: LoginComponent }, + { path: 'registration', component: RegistrationComponent }, + { path: 'registration/:id', component: ConfirmRegistrationComponent }, + { path: '', redirectTo: 'login', pathMatch: 'full' }, +]; + +@NgModule({ + declarations: [ + LoginComponent, + RegistrationComponent, + ConfirmRegistrationComponent + ], + exports: [ + LoginComponent, + RegistrationComponent, + ConfirmRegistrationComponent + ], + imports: [ + RouterModule.forChild(routes), + CommonModule, + ReactiveFormsModule + ] +}) +export class AuthModule { } diff --git a/src/app/core/auth/components/confirm-registration/confirm-registration.component.html b/src/app/core/auth/components/confirm-registration/confirm-registration.component.html new file mode 100644 index 0000000..6465355 --- /dev/null +++ b/src/app/core/auth/components/confirm-registration/confirm-registration.component.html @@ -0,0 +1 @@ +

confirm-registration works!

diff --git a/src/app/core/auth/components/confirm-registration/confirm-registration.component.scss b/src/app/core/auth/components/confirm-registration/confirm-registration.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/core/auth/components/confirm-registration/confirm-registration.component.ts b/src/app/core/auth/components/confirm-registration/confirm-registration.component.ts new file mode 100644 index 0000000..977a152 --- /dev/null +++ b/src/app/core/auth/components/confirm-registration/confirm-registration.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-confirm-registration', + templateUrl: './confirm-registration.component.html', + styleUrls: ['./confirm-registration.component.scss'] +}) +export class ConfirmRegistrationComponent { + +} diff --git a/src/app/core/auth/components/login/login.component.html b/src/app/core/auth/components/login/login.component.html new file mode 100644 index 0000000..a8bcc95 --- /dev/null +++ b/src/app/core/auth/components/login/login.component.html @@ -0,0 +1,24 @@ +
+
+ +
+

+ Beekeeper +

+

+ Anmeldung +

+
+ +
+
+ + +
+
+ + +

Neu hier? Jetzt registrieren!

+
+ +
diff --git a/src/app/core/auth/components/login/login.component.scss b/src/app/core/auth/components/login/login.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/core/auth/components/login/login.component.ts b/src/app/core/auth/components/login/login.component.ts new file mode 100644 index 0000000..d750d33 --- /dev/null +++ b/src/app/core/auth/components/login/login.component.ts @@ -0,0 +1,35 @@ +import { Component } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { filter } from 'rxjs'; +import { AuthService } from 'src/app/core/services/auth.service'; + +@Component({ + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.scss'] +}) +export class LoginComponent { + loginForm = new FormGroup({ + identifier: new FormControl('', [Validators.required]), + password: new FormControl('', [Validators.required]), + }); + + constructor( + private authService: AuthService, + private router: Router + ) { + this.authService.currentState$.pipe( + filter(state => state !== undefined) + ).subscribe(state => { + this.router.navigateByUrl('/home'); + }); + } + + login(): void { + this.authService.login({ + identifier: this.loginForm.value.identifier!, + password: this.loginForm.value.password! + }); + } +} diff --git a/src/app/core/auth/components/registration/registration.component.html b/src/app/core/auth/components/registration/registration.component.html new file mode 100644 index 0000000..d2f7abe --- /dev/null +++ b/src/app/core/auth/components/registration/registration.component.html @@ -0,0 +1 @@ +

registration works!

diff --git a/src/app/core/auth/components/registration/registration.component.scss b/src/app/core/auth/components/registration/registration.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/core/auth/components/registration/registration.component.ts b/src/app/core/auth/components/registration/registration.component.ts new file mode 100644 index 0000000..2330af4 --- /dev/null +++ b/src/app/core/auth/components/registration/registration.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-registration', + templateUrl: './registration.component.html', + styleUrls: ['./registration.component.scss'] +}) +export class RegistrationComponent { + +} diff --git a/src/app/core/components/home/home.component.html b/src/app/core/components/home/home.component.html index bd16462..d696ec9 100644 --- a/src/app/core/components/home/home.component.html +++ b/src/app/core/components/home/home.component.html @@ -1,5 +1,5 @@
- Home +
\ No newline at end of file diff --git a/src/app/core/components/navigation/navigation.component.html b/src/app/core/components/navigation/navigation.component.html index a658057..83bac92 100644 --- a/src/app/core/components/navigation/navigation.component.html +++ b/src/app/core/components/navigation/navigation.component.html @@ -1,5 +1,47 @@ - -

- Navigation -

-
\ No newline at end of file + + + diff --git a/src/app/core/components/navigation/navigation.component.ts b/src/app/core/components/navigation/navigation.component.ts index 6c2fd97..481ce3a 100644 --- a/src/app/core/components/navigation/navigation.component.ts +++ b/src/app/core/components/navigation/navigation.component.ts @@ -1,9 +1,36 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { initFlowbite } from 'flowbite'; +import { AuthService } from '../../services/auth.service'; +import { UserStateResponse } from '../../models/user-state-request.model'; +import { Router } from '@angular/router'; +import { filter, map } from 'rxjs'; @Component({ selector: 'app-navigation', templateUrl: './navigation.component.html', styleUrls: ['./navigation.component.scss'], }) -export class NavigationComponent { +export class NavigationComponent implements OnInit { + + state: UserStateResponse | undefined | null; + + constructor( + private authService: AuthService, + private router: Router, + ) { + this.state = this.authService.currentState$.value; + this.authService.currentState$.pipe( + filter(state => state === undefined) + ).subscribe( state => + this.router.navigateByUrl('/auth/login') + ); + } + + ngOnInit(): void { + initFlowbite(); + } + + logout(): void { + this.authService.logout(); + } } diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 2d58380..7b4ea58 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -2,6 +2,9 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HomeComponent } from './components/home/home.component'; import { NavigationComponent } from './components/navigation/navigation.component'; +import { AuthGuard } from './guards/auth.guard'; +import { AuthService } from './services/auth.service'; +import { RequestService } from './services/request.service'; @@ -10,6 +13,7 @@ import { NavigationComponent } from './components/navigation/navigation.componen exports: [HomeComponent, NavigationComponent], imports: [ CommonModule - ] + ], + providers: [AuthGuard, AuthService, RequestService] }) export class CoreModule { } diff --git a/src/app/core/guards/auth.guard.ts b/src/app/core/guards/auth.guard.ts new file mode 100644 index 0000000..22b69c6 --- /dev/null +++ b/src/app/core/guards/auth.guard.ts @@ -0,0 +1,26 @@ +import { Injectable } from "@angular/core"; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from "@angular/router"; +import { filter, map, Observable } from "rxjs"; +import { AuthService } from "../services/auth.service"; + +@Injectable() +export class AuthGuard implements CanActivate { + + constructor( + private authService: AuthService, + private router: Router + ) { + } + + canActivate(): Observable | boolean { + return this.authService.currentState$.pipe( + map((currentState) => { + if (!currentState) { + this.router.navigateByUrl('/auth'); + return false; + } + return true; + }) + ); + } +} \ No newline at end of file diff --git a/src/app/core/models/login-request.model copy.ts b/src/app/core/models/login-request.model copy.ts new file mode 100644 index 0000000..679a754 --- /dev/null +++ b/src/app/core/models/login-request.model copy.ts @@ -0,0 +1,8 @@ +export interface LoginRequest { + identifier: string, + password: string, +} + +export interface LoginResponse { + sessionId: string +} \ No newline at end of file diff --git a/src/app/core/models/user-state-request.model.ts b/src/app/core/models/user-state-request.model.ts new file mode 100644 index 0000000..64d1f79 --- /dev/null +++ b/src/app/core/models/user-state-request.model.ts @@ -0,0 +1,12 @@ +export interface UserStateRequest { +} + +export interface UserStateResponse { + id: string, + sessionId: string, + username: string, + roleIdentifier: string, + createdAt: string, + updatedAt: string, + permissions: string[], +} \ No newline at end of file diff --git a/src/app/core/services/auth.service.ts b/src/app/core/services/auth.service.ts new file mode 100644 index 0000000..c7c7e7a --- /dev/null +++ b/src/app/core/services/auth.service.ts @@ -0,0 +1,56 @@ +import { Injectable } from "@angular/core"; +import { RequestService } from "./request.service"; +import { UserStateResponse } from "../models/user-state-request.model"; +import { BehaviorSubject, Observable } from "rxjs"; +import { LoginRequest, LoginResponse } from "../models/login-request.model copy"; +import { Router } from "@angular/router"; + +@Injectable() +export class AuthService { + + currentState$ = new BehaviorSubject(undefined); + + constructor( + private requestService: RequestService, + private router: Router + ) { + } + + readUserState(): void { + this.requestService.get( + 'user/state', + {}, + (response: UserStateResponse) => { + this.currentState$.next(response); + }, + () => { + this.currentState$.next(undefined); + } + ) + } + + login(body: LoginRequest): LoginResponse|null { + let result = null; + + this.requestService.post( + 'auth/login-user', + body, + (response: LoginResponse) => { + result = response; + this.readUserState(); + } + ); + + return result; + } + + logout(): void { + this.requestService.post( + 'auth/logout-user', + {}, + (response: any) => { + this.readUserState(); + } + ); + } +} \ No newline at end of file diff --git a/src/app/core/services/request.service.ts b/src/app/core/services/request.service.ts index 38ec3fa..c390d63 100644 --- a/src/app/core/services/request.service.ts +++ b/src/app/core/services/request.service.ts @@ -1,7 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Router } from '@angular/router'; import { Injectable } from '@angular/core'; -import { MatSnackBar } from '@angular/material/snack-bar'; @Injectable({ providedIn: 'root' @@ -11,28 +10,46 @@ export class RequestService { constructor( private http: HttpClient, private router: Router, - private snackBar: MatSnackBar ) { } - post(apiPath: string, body: any, fct: Function) + public post(apiPath: string, body: any, successFunction: Function, errorFunction: Function|null = null) { let url = this.obtainUrl(apiPath); let observable = this.http.post(url, body).subscribe( (answer:any) => { - fct(answer); + successFunction(answer); }, (error:any) => { - this.handleError(error); + if (errorFunction === null) + this.handleError(error); + else + errorFunction(error); + } + ); + } + + public get(apiPath: string, body: any, successFunction: Function, errorFunction: Function|null = null) + { + let url = this.obtainUrl(apiPath); + let observable = this.http.get(url, body).subscribe( + (answer:any) => { + successFunction(answer); + }, + (error:any) => { + if (errorFunction === null) + this.handleError(error); + else + errorFunction(error); } ); } - postFiles(apiPath: string, files: File[], fct: Function) { + public postFiles(apiPath: string, files: File[], successFunction: Function, errorFunction: Function|null = null) { if (!files || files.length === 0) { throw 'Need to select at least one file'; } - + const formData = new FormData(); for (let fileIndex = 0; fileIndex < files.length; fileIndex++) { @@ -42,23 +59,13 @@ export class RequestService { let url = this.obtainUrl(apiPath); let observable = this.http.post(url, formData).subscribe( (answer: any) => { - fct(answer); + successFunction(answer); }, (error: any) => { - this.handleError(error); - } - ); - } - - get(apiPath: string, body: any, fct: Function) - { - let url = this.obtainUrl(apiPath); - let observable = this.http.get(url, body).subscribe( - (answer:any) => { - fct(answer); - }, - (error:any) => { - this.handleError(error); + if (errorFunction === null) + this.handleError(error); + else + errorFunction(error); } ); } @@ -98,8 +105,9 @@ export class RequestService { } private showSnackBar(message: string, action?: string) { - this.snackBar.open(message.toString(), action, { + /*this.snackBar.open(message.toString(), action, { duration: 3000, - }); + });*/ + console.log(message); } } diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index b068e2c..2be9984 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -7,27 +7,6 @@ import { PaginatorComponent } from './components/paginator/paginator.component'; import { TabControlComponent } from './components/tab-control/tab-control.component'; import { TableComponent } from './components/table/table.component'; import { FormsModule } from '@angular/forms'; -import { MatInputModule } from '@angular/material/input'; -import { MatSelectModule } from '@angular/material/select'; -import { MatSidenavModule } from '@angular/material/sidenav'; -import { MatToolbarModule } from '@angular/material/toolbar'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatListModule } from '@angular/material/list'; -import { MatIconModule } from '@angular/material/icon'; -import { MatGridListModule } from '@angular/material/grid-list'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatExpansionModule } from '@angular/material/expansion'; -import { MatSlideToggleModule } from '@angular/material/slide-toggle'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatDialogModule } from '@angular/material/dialog'; -import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { MatCheckboxModule } from '@angular/material/checkbox'; -import { MatPaginatorModule } from '@angular/material/paginator'; -import { MatTabsModule } from '@angular/material/tabs'; -import { MatTableModule } from '@angular/material/table'; -import { MatChipsModule } from '@angular/material/chips'; @NgModule({ declarations: [ @@ -44,54 +23,12 @@ import { MatChipsModule } from '@angular/material/chips'; FormComponent, TableComponent, - MatSlideToggleModule, - MatCardModule, - MatButtonModule, - MatIconModule, - MatGridListModule, - MatFormFieldModule, FormsModule, - MatExpansionModule, - MatMenuModule, - MatListModule, - MatToolbarModule, - MatSidenavModule, - MatInputModule, - MatSelectModule, - MatDividerModule, - MatDialogModule, - MatSnackBarModule, - MatPaginatorModule, - MatCheckboxModule, - MatTabsModule, - MatTableModule, - MatChipsModule, ], imports: [ RouterModule, CommonModule, - MatSlideToggleModule, - MatCardModule, - MatButtonModule, - MatIconModule, - MatGridListModule, - MatFormFieldModule, FormsModule, - MatExpansionModule, - MatMenuModule, - MatListModule, - MatToolbarModule, - MatSidenavModule, - MatInputModule, - MatSelectModule, - MatDividerModule, - MatDialogModule, - MatSnackBarModule, - MatPaginatorModule, - MatCheckboxModule, - MatTabsModule, - MatTableModule, - MatChipsModule, ], }) export class SharedModule {} diff --git a/src/assets/icon.ico b/src/assets/icon.ico index c909290..dfc2768 100644 Binary files a/src/assets/icon.ico and b/src/assets/icon.ico differ diff --git a/src/assets/icon.png b/src/assets/icon.png new file mode 100644 index 0000000..286ecf9 Binary files /dev/null and b/src/assets/icon.png differ diff --git a/src/favicon.ico b/src/favicon.ico index e7ff278..dfc2768 100644 Binary files a/src/favicon.ico and b/src/favicon.ico differ diff --git a/src/index.html b/src/index.html index 9bb891a..4123f0d 100644 --- a/src/index.html +++ b/src/index.html @@ -5,11 +5,10 @@ Bienen - + - - + diff --git a/src/styles.scss b/src/styles.scss index bd6213e..cfa7bc1 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,3 +1,20 @@ @tailwind base; @tailwind components; -@tailwind utilities; \ No newline at end of file +@tailwind utilities; + +@layer base { + :root { + --color-text-primary: 0,0,0; + --color-text-primary-muted: 100,100,100; + --color-text-secondary: 255,255,255; + --color-text-secondary-muted: 170,170,170; + --color-text-accent: 255, 199, 44; + --color-text-accent-muted: 255, 199, 44; + + + --color-primary: 255, 255, 255; + --color-secondary: 255, 199, 44; + --color-accent: 255, 199, 44; + } + } + \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index 5397866..95fbc3f 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,9 +1,41 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: ["./src/**/*.{html,ts}"], - theme: { - extend: {}, - }, - plugins: [], +function withOpacity(variableName) { + return ({ opacityValue }) => { + if (opacityValue !== undefined) { + return `rgba(var(${variableName}), ${opacityValue})` + } + return `rgb(var(${variableName}))` + } +} + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./src/**/*.{html,ts}", + "./node_modules/flowbite/**/*.js" + ], + theme: { + extend: { + textColor: { + skin: { + primary: withOpacity('--color-text-primary'), + 'primary-muted': withOpacity('--color-text-primary-muted'), + secondary: withOpacity('--color-text-secondary'), + 'secondary-muted': withOpacity('--color-text-secondary-muted'), + accent: withOpacity('--color-text-accent'), + 'accent-muted': withOpacity('--color-text-accent-muted'), + }, + }, + backgroundColor: { + skin: { + primary: withOpacity('--color-primary'), + secondary: withOpacity('--color-secondary'), + accent: withOpacity('--color-accent'), + }, + }, + }, + }, + plugins: [ + require('flowbite/plugin') + ], }