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
+
+
+
+
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
+
+
+
+
+
+
+ Beekeeper
+
+
+
+
+
+
+ {{state?.username}}
+ {{state?.roleIdentifier}}
+
+
+
+
+ Open main menu
+
+
+
+
+
+
+
+
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')
+ ],
}