This commit is contained in:
Flo 2024-08-26 20:19:10 +00:00
parent 0499c81924
commit 9145bc245f
42 changed files with 839 additions and 635 deletions

View File

@ -1,18 +1,17 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from "@angular/core";
import { AuthService } from './core/services/auth.service'; import { AuthService } from "./core/services/auth.service";
import { AppService } from './core/services/app.service'; import { AppService } from "./core/services/app.service";
@Component({ @Component({
selector: 'app-root', selector: "app-root",
templateUrl: './app.component.html', templateUrl: "./app.component.html",
styleUrls: ['./app.component.scss'] styleUrls: ["./app.component.scss"],
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {
constructor( constructor(
private authService: AuthService, private authService: AuthService,
private appService: AppService, private appService: AppService
) { ) {}
}
ngOnInit(): void { ngOnInit(): void {
this.authService.readUserState(); this.authService.readUserState();

View File

@ -1,21 +1,28 @@
import { NgModule } from '@angular/core'; import { NgModule } from "@angular/core";
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from "@angular/platform-browser";
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from "@angular/common/http";
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { AppComponent } from './app.component'; import { AppComponent } from "./app.component";
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from "@angular/router";
import { SharedModule } from './shared/shared.module'; import { SharedModule } from "./shared/shared.module";
import { HomeComponent } from './core/components/home/home.component'; import { HomeComponent } from "./core/components/home/home.component";
import { CoreModule } from './core/core.module'; import { CoreModule } from "./core/core.module";
import { AuthGuard } from './core/guards/auth.guard'; import { AuthGuard } from "./core/guards/auth.guard";
import { SettingsComponent } from './core/components/settings/settings.component'; import { SettingsComponent } from "./core/components/settings/settings.component";
const routes: Routes = [ const routes: Routes = [
{ path: 'auth', loadChildren: () => import('./core/auth/auth.module').then(m => m.AuthModule) }, {
{ path: '', component: HomeComponent, canActivate: [AuthGuard], children: [ path: "auth",
{path: 'settings', component: SettingsComponent} loadChildren: () =>
]}, import("./core/auth/auth.module").then((m) => m.AuthModule),
},
{
path: "",
component: HomeComponent,
canActivate: [AuthGuard],
children: [{ path: "settings", component: SettingsComponent }],
},
]; ];
@NgModule({ @NgModule({
@ -24,6 +31,7 @@ const routes: Routes = [
BrowserModule, BrowserModule,
BrowserAnimationsModule, BrowserAnimationsModule,
HttpClientModule, HttpClientModule,
RouterModule,
RouterModule.forRoot(routes), RouterModule.forRoot(routes),
SharedModule, SharedModule,
CoreModule, CoreModule,

View File

@ -1,33 +1,32 @@
import { NgModule } from '@angular/core'; import { NgModule } from "@angular/core";
import { CommonModule } from '@angular/common'; import { CommonModule } from "@angular/common";
import { LoginComponent } from './components/login/login.component'; import { LoginComponent } from "./components/login/login.component";
import { RegistrationComponent } from './components/registration/registration.component'; import { RegistrationComponent } from "./components/registration/registration.component";
import { ConfirmRegistrationComponent } from './components/confirm-registration/confirm-registration.component'; import { ConfirmRegistrationComponent } from "./components/confirm-registration/confirm-registration.component";
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from "@angular/router";
import { ReactiveFormsModule } from '@angular/forms'; import { ReactiveFormsModule } from "@angular/forms";
const routes: Routes = [ const routes: Routes = [
{ path: 'login', component: LoginComponent }, { path: "login", component: LoginComponent },
{ path: 'registration', component: RegistrationComponent }, { path: "registration", component: RegistrationComponent },
{ path: 'registration/:registrationId', component: ConfirmRegistrationComponent }, {
{ path: '', redirectTo: 'login', pathMatch: 'full' }, path: "registration/:registrationId",
component: ConfirmRegistrationComponent,
},
{ path: "", redirectTo: "login", pathMatch: "full" },
]; ];
@NgModule({ @NgModule({
declarations: [ declarations: [
LoginComponent, LoginComponent,
RegistrationComponent, RegistrationComponent,
ConfirmRegistrationComponent ConfirmRegistrationComponent,
], ],
exports: [ exports: [
LoginComponent, LoginComponent,
RegistrationComponent, RegistrationComponent,
ConfirmRegistrationComponent ConfirmRegistrationComponent,
], ],
imports: [ imports: [RouterModule.forChild(routes), CommonModule, ReactiveFormsModule],
RouterModule.forChild(routes),
CommonModule,
ReactiveFormsModule
]
}) })
export class AuthModule { } export class AuthModule {}

View File

@ -1,5 +1,4 @@
<div class="mb-10"> <div class="mb-10"></div>
</div>
<div class="max-w-sm mx-auto mb-10"> <div class="max-w-sm mx-auto mb-10">
<h1 class="font-bold text-center text-skin-primary text-5xl mb-5"> <h1 class="font-bold text-center text-skin-primary text-5xl mb-5">
@ -12,12 +11,39 @@
<form class="max-w-sm mx-auto" [formGroup]="confirmRegistrationForm"> <form class="max-w-sm mx-auto" [formGroup]="confirmRegistrationForm">
<div class="mb-5"> <div class="mb-5">
<label for="password" class="block mb-2 text-sm font-medium text-skin-primary-muted">Passwort</label> <label
<input formControlName="password" type="password" id="password" class="bg-skin-primary border border-gray-300 text-skin-primary text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" required /> for="password"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>Passwort</label
>
<input
formControlName="password"
type="password"
id="password"
class="bg-skin-primary border border-gray-300 text-skin-primary text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
required
/>
</div> </div>
<div class="mb-5"> <div class="mb-5">
<label for="passwordConfirmation" class="block mb-2 text-sm font-medium text-skin-primary-muted">Passwort wiederholen</label> <label
<input formControlName="passwordConfirmation" type="password" id="passwordConfirmation" class="bg-skin-primary border border-gray-300 text-skin-primary text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" required /> for="passwordConfirmation"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>Passwort wiederholen</label
>
<input
formControlName="passwordConfirmation"
type="password"
id="passwordConfirmation"
class="bg-skin-primary border border-gray-300 text-skin-primary text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
required
/>
</div> </div>
<button (click)="confirm()" [disabled]="!confirmRegistrationForm.valid" type="submit" class="w-full 9xl:w-auto font-bold text-skin-secondary bg-skin-accent hover:text-skin-primary rounded-lg text-sm px-5 py-2.5 text-center">Registrieren</button> <button
(click)="confirm()"
[disabled]="!confirmRegistrationForm.valid"
type="submit"
class="w-full 9xl:w-auto font-bold text-skin-secondary bg-skin-accent hover:text-skin-primary rounded-lg text-sm px-5 py-2.5 text-center"
>
Registrieren
</button>
</form> </form>

View File

@ -1,18 +1,18 @@
import { Component } from '@angular/core'; import { Component } from "@angular/core";
import { FormControl, FormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from "@angular/forms";
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from "@angular/router";
import { filter } from 'rxjs'; import { filter } from "rxjs";
import { AuthService } from 'src/app/core/services/auth.service'; import { AuthService } from "src/app/core/services/auth.service";
@Component({ @Component({
selector: 'app-confirm-registration', selector: "app-confirm-registration",
templateUrl: './confirm-registration.component.html', templateUrl: "./confirm-registration.component.html",
styleUrls: ['./confirm-registration.component.scss'] styleUrls: ["./confirm-registration.component.scss"],
}) })
export class ConfirmRegistrationComponent { export class ConfirmRegistrationComponent {
confirmRegistrationForm = new FormGroup({ confirmRegistrationForm = new FormGroup({
password: new FormControl('', [Validators.required]), password: new FormControl("", [Validators.required]),
passwordConfirmation: new FormControl('', [Validators.required]), passwordConfirmation: new FormControl("", [Validators.required]),
}); });
registrationId: string | undefined; registrationId: string | undefined;
@ -22,27 +22,23 @@ export class ConfirmRegistrationComponent {
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
private router: Router private router: Router
) { ) {
this.activatedRoute.params.subscribe( this.activatedRoute.params.subscribe((params) => {
(params) => { this.registrationId = params["registrationId"];
this.registrationId = params['registrationId']; });
}
);
this.authService.currentState$.pipe( this.authService.currentState$
filter(state=>state !== undefined && state !== null) .pipe(filter((state) => state !== undefined && state !== null))
).subscribe((state) => .subscribe((state) => this.router.navigateByUrl("/"));
this.router.navigateByUrl('/')
);
} }
confirm(): void { confirm(): void {
if (this.registrationId === undefined) if (this.registrationId === undefined) return;
return;
this.authService.confirmRegistration({ this.authService.confirmRegistration({
id: this.registrationId!, id: this.registrationId!,
password: this.confirmRegistrationForm.value.password!, password: this.confirmRegistrationForm.value.password!,
passwordConfirmation: this.confirmRegistrationForm.value.passwordConfirmation! passwordConfirmation:
this.confirmRegistrationForm.value.passwordConfirmation!,
}); });
} }
} }

View File

@ -1,24 +1,59 @@
<div class="mb-10"> <div class="mb-10"></div>
</div>
<div class="max-w-sm mx-auto mb-10"> <div class="max-w-sm mx-auto mb-10">
<h1 class="font-bold text-center text-skin-primary text-5xl mb-5"> <h1 class="font-bold text-center text-skin-primary text-5xl mb-5">
Beekeeper Beekeeper
</h1> </h1>
<h1 class="font-bold text-center text-skin-accent text-xl"> <h1 class="font-bold text-center text-skin-accent text-xl">Anmeldung</h1>
Anmeldung
</h1>
</div> </div>
<form class="max-w-sm mx-auto" [formGroup]="loginForm"> <form class="max-w-sm mx-auto" [formGroup]="loginForm">
<div class="mb-5"> <div class="mb-5">
<label for="identifier" class="block mb-2 text-sm font-medium text-skin-primary-muted">Benutzername oder E-Mail</label> <label
<input formControlName="identifier" type="text" id="identifier" class="bg-skin-primary border border-gray-300 text-skin-primary text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" placeholder="username72 / your@email.com" required /> for="identifier"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>Benutzername oder E-Mail</label
>
<input
formControlName="identifier"
type="text"
id="identifier"
class="bg-skin-primary border border-gray-300 text-skin-primary text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
placeholder="username72 / your@email.com"
required
/>
</div> </div>
<div class="mb-5"> <div class="mb-5">
<label for="password" class="block mb-2 text-sm font-medium text-skin-primary-muted">Passwort</label> <label
<input formControlName="password" type="password" id="password" class="bg-skin-primary border border-gray-300 text-skin-primary text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" required /> for="password"
<p id="helper-text-explanation" class="mt-2 text-sm text-skin-primary-muted">Neu hier? <a routerLink="/auth/registration" class="font-medium text-skin-accent hover:underline hover:font-bold">Jetzt registrieren!</a></p> class="block mb-2 text-sm font-medium text-skin-primary-muted"
>Passwort</label
>
<input
formControlName="password"
type="password"
id="password"
class="bg-skin-primary border border-gray-300 text-skin-primary text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
required
/>
<p
id="helper-text-explanation"
class="mt-2 text-sm text-skin-primary-muted"
>
Neu hier?
<a
routerLink="/auth/registration"
class="font-medium text-skin-accent hover:underline hover:font-bold"
>Jetzt registrieren!</a
>
</p>
</div> </div>
<button (click)="login()" [disabled]="!loginForm.valid" type="submit" class="w-full 9xl:w-auto font-bold text-skin-secondary bg-skin-accent hover:text-skin-primary rounded-lg text-sm px-5 py-2.5 text-center">Anmelden</button> <button
(click)="login()"
[disabled]="!loginForm.valid"
type="submit"
class="w-full 9xl:w-auto font-bold text-skin-secondary bg-skin-accent hover:text-skin-primary rounded-lg text-sm px-5 py-2.5 text-center"
>
Anmelden
</button>
</form> </form>

View File

@ -1,35 +1,30 @@
import { Component } from '@angular/core'; import { Component } from "@angular/core";
import { FormControl, FormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from "@angular/forms";
import { Router } from '@angular/router'; import { Router } from "@angular/router";
import { filter, map } from 'rxjs'; import { filter, map } from "rxjs";
import { AuthService } from 'src/app/core/services/auth.service'; import { AuthService } from "src/app/core/services/auth.service";
@Component({ @Component({
selector: 'app-login', selector: "app-login",
templateUrl: './login.component.html', templateUrl: "./login.component.html",
styleUrls: ['./login.component.scss'] styleUrls: ["./login.component.scss"],
}) })
export class LoginComponent { export class LoginComponent {
loginForm = new FormGroup({ loginForm = new FormGroup({
identifier: new FormControl('', [Validators.required]), identifier: new FormControl("", [Validators.required]),
password: new FormControl('', [Validators.required]), password: new FormControl("", [Validators.required]),
}); });
constructor( constructor(private authService: AuthService, private router: Router) {
private authService: AuthService, this.authService.currentState$
private router: Router .pipe(filter((state) => state !== undefined && state !== null))
) { .subscribe((state) => this.router.navigateByUrl("/"));
this.authService.currentState$.pipe(
filter(state=>state !== undefined && state !== null)
).subscribe((state) =>
this.router.navigateByUrl('/')
);
} }
login(): void { login(): void {
this.authService.login({ this.authService.login({
identifier: this.loginForm.value.identifier!, identifier: this.loginForm.value.identifier!,
password: this.loginForm.value.password! password: this.loginForm.value.password!,
}); });
} }
} }

View File

@ -1,24 +1,59 @@
<div class="mb-10"> <div class="mb-10"></div>
</div>
<div class="max-w-sm mx-auto mb-10"> <div class="max-w-sm mx-auto mb-10">
<h1 class="font-bold text-center text-skin-primary text-5xl mb-5"> <h1 class="font-bold text-center text-skin-primary text-5xl mb-5">
Beekeeper Beekeeper
</h1> </h1>
<h1 class="font-bold text-center text-skin-accent text-xl"> <h1 class="font-bold text-center text-skin-accent text-xl">Registrierung</h1>
Registrierung
</h1>
</div> </div>
<form class="max-w-sm mx-auto" [formGroup]="registrationForm"> <form class="max-w-sm mx-auto" [formGroup]="registrationForm">
<div class="mb-5"> <div class="mb-5">
<label for="mail" class="block mb-2 text-sm font-medium text-skin-primary-muted">E-Mail</label> <label
<input formControlName="mail" type="mail" id="mail" class="bg-skin-primary border border-gray-300 text-skin-primary text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" placeholder="your@email.com" required /> for="mail"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>E-Mail</label
>
<input
formControlName="mail"
type="mail"
id="mail"
class="bg-skin-primary border border-gray-300 text-skin-primary text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
placeholder="your@email.com"
required
/>
</div> </div>
<div class="mb-5"> <div class="mb-5">
<label for="username" class="block mb-2 text-sm font-medium text-skin-primary-muted">Benutzername</label> <label
<input formControlName="username" type="string" id="username" class="bg-skin-primary border border-gray-300 text-skin-primary text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" required /> for="username"
<p id="helper-text-explanation" class="mt-2 text-sm text-skin-primary-muted">Bereits registiert? <a routerLink="/auth/login" class="font-medium text-skin-accent hover:underline hover:font-bold">Jetzt anmelden!</a></p> class="block mb-2 text-sm font-medium text-skin-primary-muted"
>Benutzername</label
>
<input
formControlName="username"
type="string"
id="username"
class="bg-skin-primary border border-gray-300 text-skin-primary text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
required
/>
<p
id="helper-text-explanation"
class="mt-2 text-sm text-skin-primary-muted"
>
Bereits registiert?
<a
routerLink="/auth/login"
class="font-medium text-skin-accent hover:underline hover:font-bold"
>Jetzt anmelden!</a
>
</p>
</div> </div>
<button (click)="register()" [disabled]="!registrationForm.valid" type="submit" class="w-full 9xl:w-auto font-bold text-skin-secondary bg-skin-accent hover:text-skin-primary rounded-lg text-sm px-5 py-2.5 text-center">Registrieren</button> <button
(click)="register()"
[disabled]="!registrationForm.valid"
type="submit"
class="w-full 9xl:w-auto font-bold text-skin-secondary bg-skin-accent hover:text-skin-primary rounded-lg text-sm px-5 py-2.5 text-center"
>
Registrieren
</button>
</form> </form>

View File

@ -1,37 +1,32 @@
import { Component } from '@angular/core'; import { Component } from "@angular/core";
import { FormControl, FormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from "@angular/forms";
import { Router } from '@angular/router'; import { Router } from "@angular/router";
import { filter } from 'rxjs'; import { filter } from "rxjs";
import { AuthService } from 'src/app/core/services/auth.service'; import { AuthService } from "src/app/core/services/auth.service";
@Component({ @Component({
selector: 'app-registration', selector: "app-registration",
templateUrl: './registration.component.html', templateUrl: "./registration.component.html",
styleUrls: ['./registration.component.scss'] styleUrls: ["./registration.component.scss"],
}) })
export class RegistrationComponent { export class RegistrationComponent {
registrationForm = new FormGroup({ registrationForm = new FormGroup({
mail: new FormControl('', [Validators.required]), mail: new FormControl("", [Validators.required]),
username: new FormControl('', [Validators.required]), username: new FormControl("", [Validators.required]),
}); });
constructor( constructor(private authService: AuthService, private router: Router) {
private authService: AuthService, this.authService.currentState$
private router: Router .pipe(filter((state) => state !== undefined && state !== null))
) { .subscribe((state) => this.router.navigateByUrl("/"));
this.authService.currentState$.pipe(
filter(state=>state !== undefined && state !== null)
).subscribe((state) =>
this.router.navigateByUrl('/')
);
} }
register(): void { register(): void {
this.authService.register({ this.authService.register({
mail: this.registrationForm.value.mail!, mail: this.registrationForm.value.mail!,
username: this.registrationForm.value.username! username: this.registrationForm.value.username!,
}); });
this.router.navigateByUrl('/auth/login'); this.router.navigateByUrl("/auth/login");
} }
} }

View File

@ -1,26 +1,5 @@
<app-navigation></app-navigation> <app-navigation></app-navigation>
<div class="max-w-screen-xl mx-auto p-4"> <div class="max-w-screen-xl mx-auto p-4">
<shared-card icon="/assets/icon.png" header="Test" subHeader="lol noch ein test">
I'm working
</shared-card>
<shared-card icon="/assets/icon.png" header="Test" >
I'm working
</shared-card>
<shared-card>
I'm working
</shared-card>
<div class="mb-5">
<shared-table [items]="colonies" [columns]="columns">
</shared-table>
</div>
<shared-paginator total="20" perPage="5" />
<router-outlet></router-outlet> <router-outlet></router-outlet>
</div> </div>

View File

@ -1,40 +1,8 @@
import { Component } from '@angular/core'; import { Component } from "@angular/core";
import { RequestService } from 'src/app/core/services/request.service';
import { ColumnDefinition } from 'src/app/shared/components/table/table.component';
interface Colony {
name: string;
}
@Component({ @Component({
selector: 'app-home', selector: "app-home",
templateUrl: './home.component.html', templateUrl: "./home.component.html",
styleUrls: ['./home.component.scss'], styleUrls: ["./home.component.scss"],
}) })
export class HomeComponent { export class HomeComponent {}
columns: ColumnDefinition[] = [
{
header: 'Name',
columnFunction: (colony:Colony) => colony.name,
routerLink: (colony:Colony) => '#',
},
{
header: 'Name2',
columnFunction: (colony:Colony) => colony.name,
}
];
colonies: Colony[] = [
{
name: 'Die Römer'
},
{
name: 'Die Griechen'
}
];
constructor(public requestService: RequestService) {
}
}

View File

@ -1,60 +1,105 @@
<nav class="bg-skin-accent"> <nav class="bg-skin-accent">
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4"> <div
class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4"
>
<!-- Title --> <!-- Title -->
<a href="#" class="flex items-center space-x-3"> <a routerLink="/" class="flex items-center space-x-3">
<img src="assets/icon.png" class="h-10" alt="Beekeeper Logo" /> <img src="assets/icon.png" class="h-10" alt="Beekeeper Logo" />
<span class="text-skin-primary self-center text-4xl font-semibold whitespace-nowrap">Beekeeper</span> <span
class="text-skin-primary self-center text-4xl font-semibold whitespace-nowrap"
>Bienenkeeper</span
>
</a> </a>
<div class="flex items-center md:order-2 space-x-3 md:space-x-0"> <div class="flex items-center md:order-2 space-x-3 md:space-x-0">
<!--User Bubble--> <!--User Bubble-->
<button type="button" class="block w-10 h-10 text-sm bg-skin-primary rounded-full md:me-0 focus:ring-4 focus:ring-skin-primary" id="user-menu-button" aria-expanded="false" data-dropdown-toggle="user-dropdown" data-dropdown-placement="bottom"> <button
<span class="text-skin-accent rounded-full font-bold p-2">AA</span> type="button"
<!-- <img class="w-8 h-8 rounded-full" src="/docs/images/people/profile-picture-3.jpg" alt="user photo"> --> class="block w-10 h-10 text-sm bg-skin-primary rounded-full md:me-0 focus:ring-4 focus:ring-skin-primary"
id="user-menu-button"
aria-expanded="false"
data-dropdown-toggle="user-dropdown"
data-dropdown-placement="bottom"
>
<span class="text-skin-accent rounded-full font-bold p-2 uppercase">{{
state?.username[0] + state?.roleIdentifier[0]
}}</span>
</button> </button>
<!-- User dropdown -->
<div
class="z-50 hidden my-4 text-base list-none divide-y divide-skin-primary rounded-lg shadow-sm shadow-skin-primary bg-skin-primary"
id="user-dropdown"
>
<div class="px-4 py-3">
<span class="block text-sm text-skin-accent font-bold">{{
state?.username
}}</span>
<span class="block text-sm text-skin-primary-muted truncate">{{
state?.roleIdentifier
}}</span>
</div>
<ul class="p-4" aria-labelledby="user-menu-button">
<li *ngFor="let usermenuButton of usermenuButtons">
<button
[routerLink]="
usermenuButton.routerLink === undefined
? null
: [usermenuButton.routerLink]
"
class="w-full text-left block py-2 px-3 rounded text-skin-primary hover:bg-skin-accent"
(click)="clickUserMenuButtonLabel(usermenuButton)"
>
{{ getUserMenuButtonLabel(usermenuButton) }}
</button>
</li>
</ul>
</div>
<!--Burger Menu--> <!--Burger Menu-->
<button data-collapse-toggle="navbar-user" type="button" class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-skin-primary rounded-lg md:hidden hover:bg-skin-primary" aria-controls="navbar-user" aria-expanded="false"> <button
data-collapse-toggle="navbar-user"
type="button"
class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-skin-primary rounded-lg md:hidden hover:bg-skin-primary"
aria-controls="navbar-user"
aria-expanded="false"
>
<span class="sr-only">Open main menu</span> <span class="sr-only">Open main menu</span>
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14"> <svg
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"/> class="w-5 h-5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 17 14"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M1 1h15M1 7h15M1 13h15"
/>
</svg> </svg>
</button> </button>
</div> </div>
<!-- User dropdown -->
<div class="z-50 hidden my-4 text-base list-none divide-y divide-skin-primary rounded-lg shadow-sm shadow-skin-primary bg-skin-primary" id="user-dropdown">
<div class="px-4 py-3">
<span class="block text-sm text-skin-accent font-bold">{{state?.username}}</span>
<span class="block text-sm text-skin-primary-muted truncate">{{state?.roleIdentifier}}</span>
</div>
<ul class="p-4" aria-labelledby="user-menu-button">
<li>
<a href="/settings" class="block py-2 px-3 rounded text-skin-primary hover:bg-skin-accent">Einstellungen</a>
</li>
<li>
<button class="w-full block py-2 px-3 rounded text-skin-primary hover:bg-skin-accent" (click)="toggleDarkmode()">{{ darkMode ? 'Hell' : 'Dunkel' }}</button>
</li>
<li>
<button class="w-full block py-2 px-3 rounded text-skin-primary hover:bg-skin-accent" (click)="logout()">Ausloggen</button>
</li>
</ul>
</div>
<!-- Navigatoin --> <!-- Navigatoin -->
<div class="items-center justify-between hidden w-full md:flex md:w-auto md:order-1" id="navbar-user"> <div
<ul class="flex flex-col text-xl p-4 md:p-0 mt-4 rounded-lg bg-skin-primary md:bg-skin-accent md:space-x-8 md:flex-row md:mt-0 md:border-0"> class="items-center justify-between hidden w-full md:flex md:w-auto md:order-1"
<li> id="navbar-user"
<a routerLink="/home" class="block py-2 px-3 rounded text-skin-primary md:p-0" aria-current="page">Home</a> >
</li> <ul
<li> class="flex flex-col text-xl p-4 md:p-0 mt-4 rounded-lg bg-skin-primary md:bg-skin-accent md:space-x-8 md:flex-row md:mt-0 md:border-0"
<a href="#" class="block py-2 px-3 rounded text-skin-primary-muted hover:text-skin-primary hover:bg-skin-accent md:p-0">About</a> >
<li *ngFor="let navigationLink of navigationLinks">
<a
class="block py-2 px-3 rounded text-skin-primary hover:font-bold hover:bg-skin-accent md:p-0"
[routerLink]="navigationLink.routerLink"
[routerLinkActiveOptions]="{ exact: true }"
routerLinkActive="font-bold"
>{{ navigationLink.label }}</a
>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
</nav> </nav>

View File

@ -1,17 +1,54 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from "@angular/core";
import { initFlowbite } from 'flowbite'; import { initFlowbite } from "flowbite";
import { AuthService } from '../../services/auth.service'; import { AuthService } from "../../services/auth.service";
import { UserStateResponse } from '../../models/user-state-request.model'; import { UserStateResponse } from "../../models/user-state-request.model";
import { Router } from '@angular/router'; import { Router } from "@angular/router";
import { filter, map } from 'rxjs'; import { filter, map } from "rxjs";
import { AppService } from '../../services/app.service'; import { AppService } from "../../services/app.service";
interface NavigationLink {
label: string;
routerLink: string;
}
interface UserMenuButton {
label: Function | string;
routerLink: string | undefined;
clickCallback: Function | undefined;
}
@Component({ @Component({
selector: 'app-navigation', selector: "app-navigation",
templateUrl: './navigation.component.html', templateUrl: "./navigation.component.html",
styleUrls: ['./navigation.component.scss'], styleUrls: ["./navigation.component.scss"],
}) })
export class NavigationComponent implements OnInit { export class NavigationComponent implements OnInit {
navigationLinks: NavigationLink[] = [
{ routerLink: "/", label: "Startseite" },
{ routerLink: "/settings", label: "Völker" },
];
usermenuButtons: UserMenuButton[] = [
{
label: "Einstellungen",
routerLink: "/settings",
clickCallback: undefined,
},
{
label: () => (this.darkMode ? "Hell" : "Dunkel"),
routerLink: undefined,
clickCallback: () => {
this.toggleDarkmode();
},
},
{
label: "Ausloggen",
routerLink: undefined,
clickCallback: () => {
this.logout();
},
},
];
state: UserStateResponse | undefined | null; state: UserStateResponse | undefined | null;
darkMode: boolean; darkMode: boolean;
@ -19,14 +56,14 @@ export class NavigationComponent implements OnInit {
constructor( constructor(
private authService: AuthService, private authService: AuthService,
private appService: AppService, private appService: AppService,
private router: Router, private router: Router
) { ) {
this.darkMode = this.appService.darkMode; this.darkMode = this.appService.darkMode;
this.state = this.authService.currentState$.value; this.state = this.authService.currentState$.value;
this.authService.currentState$.pipe( this.authService.currentState$
filter(state => state === undefined) .pipe(filter((state) => state === undefined))
).subscribe( state =>{ .subscribe((state) => {
this.router.navigateByUrl('/auth/login'); this.router.navigateByUrl("/auth/login");
}); });
} }
@ -42,4 +79,18 @@ export class NavigationComponent implements OnInit {
this.appService.toggleDarkMode(); this.appService.toggleDarkMode();
this.darkMode = this.appService.darkMode; this.darkMode = this.appService.darkMode;
} }
clickUserMenuButtonLabel(button: UserMenuButton) {
if (button.clickCallback !== undefined) {
button.clickCallback();
}
}
getUserMenuButtonLabel(button: UserMenuButton) {
if (typeof button.label === "function") {
return button.label();
} else {
return button.label;
}
}
} }

View File

@ -1,33 +1,57 @@
<div> <div>
<div class="mb-4 border-b border-gray-200 dark:border-gray-700"> <div class="mb-4 border-b border-skin-primary">
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center" id="default-styled-tab" data-tabs-toggle="#default-styled-tab-content" data-tabs-active-classes="text-purple-600 hover:text-purple-600 dark:text-purple-500 dark:hover:text-purple-500 border-purple-600 dark:border-purple-500" data-tabs-inactive-classes="dark:border-transparent text-gray-500 hover:text-gray-600 dark:text-gray-400 border-gray-100 hover:border-gray-300 dark:border-gray-700 dark:hover:text-gray-300" role="tablist"> <ul
class="flex flex-wrap -mb-px text-sm font-medium text-center"
id="default-styled-tab"
data-tabs-toggle="#default-styled-tab-content"
data-tabs-active-classes="text-skin-accent border-skin-accent"
data-tabs-inactive-classes="text-skin-primary-muted hover:text-skin-primary border-skin-primary hover:border-skin-accent"
role="tablist"
>
<li class="me-2" role="presentation"> <li class="me-2" role="presentation">
<button class="inline-block p-4 border-b-2 rounded-t-lg" id="profile-styled-tab" data-tabs-target="#styled-profile" type="button" role="tab" aria-controls="profile" aria-selected="false">Profile</button> <button
class="inline-block p-4 border-b-2 rounded-t-lg"
id="profile-styled-tab"
data-tabs-target="#styled-profile"
type="button"
role="tab"
aria-controls="profile"
aria-selected="false"
>
Profil
</button>
</li> </li>
<li class="me-2" role="presentation"> <li class="me-2" role="presentation">
<button class="inline-block p-4 border-b-2 rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300" id="dashboard-styled-tab" data-tabs-target="#styled-dashboard" type="button" role="tab" aria-controls="dashboard" aria-selected="false">Dashboard</button> <button
</li> class="inline-block p-4 border-b-2 rounded-t-lg"
<li class="me-2" role="presentation"> id="security-styled-tab"
<button class="inline-block p-4 border-b-2 rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300" id="settings-styled-tab" data-tabs-target="#styled-settings" type="button" role="tab" aria-controls="settings" aria-selected="false">Settings</button> data-tabs-target="#styled-security"
</li> type="button"
<li role="presentation"> role="tab"
<button class="inline-block p-4 border-b-2 rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300" id="contacts-styled-tab" data-tabs-target="#styled-contacts" type="button" role="tab" aria-controls="contacts" aria-selected="false">Contacts</button> aria-controls="security"
aria-selected="false"
>
Sicherheit
</button>
</li> </li>
</ul> </ul>
</div> </div>
<div id="default-styled-tab-content"> <div id="default-styled-tab-content" class="text-skin-primary">
<div class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800" id="styled-profile" role="tabpanel" aria-labelledby="profile-tab"> <div
<p class="text-sm text-gray-500 dark:text-gray-400">This is some placeholder content the <strong class="font-medium text-gray-800 dark:text-white">Profile tab's associated content</strong>. Clicking another tab will toggle the visibility of this one for the next. The tab JavaScript swaps classes to control the content visibility and styling.</p> class="hidden p-4 rounded-lg"
id="styled-profile"
role="tabpanel"
aria-labelledby="profile-tab"
>
<app-tab-profile />
</div> </div>
<div class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800" id="styled-dashboard" role="tabpanel" aria-labelledby="dashboard-tab"> <div
<p class="text-sm text-gray-500 dark:text-gray-400">This is some placeholder content the <strong class="font-medium text-gray-800 dark:text-white">Dashboard tab's associated content</strong>. Clicking another tab will toggle the visibility of this one for the next. The tab JavaScript swaps classes to control the content visibility and styling.</p> class="hidden p-4 rounded-lg"
</div> id="styled-security"
<div class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800" id="styled-settings" role="tabpanel" aria-labelledby="settings-tab"> role="tabpanel"
<p class="text-sm text-gray-500 dark:text-gray-400">This is some placeholder content the <strong class="font-medium text-gray-800 dark:text-white">Settings tab's associated content</strong>. Clicking another tab will toggle the visibility of this one for the next. The tab JavaScript swaps classes to control the content visibility and styling.</p> aria-labelledby="security-tab"
</div> >
<div class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800" id="styled-contacts" role="tabpanel" aria-labelledby="contacts-tab"> <app-tab-security />
<p class="text-sm text-gray-500 dark:text-gray-400">This is some placeholder content the <strong class="font-medium text-gray-800 dark:text-white">Contacts tab's associated content</strong>. Clicking another tab will toggle the visibility of this one for the next. The tab JavaScript swaps classes to control the content visibility and styling.</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,10 +1,13 @@
import { Component } from '@angular/core'; import { Component, OnInit } from "@angular/core";
import { initFlowbite } from "flowbite";
@Component({ @Component({
selector: 'app-settings', selector: "app-settings",
templateUrl: './settings.component.html', templateUrl: "./settings.component.html",
styleUrls: ['./settings.component.scss'] styleUrls: ["./settings.component.scss"],
}) })
export class SettingsComponent { export class SettingsComponent implements OnInit {
ngOnInit(): void {
initFlowbite();
}
} }

View File

@ -0,0 +1,12 @@
<shared-card
icon="/assets/icon.png"
header="Test"
subHeader="lol noch ein test"
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ligula erat,
auctor eget dolor sed, interdum interdum quam. Donec dui nisl, dignissim quis
sollicitudin vitae, eleifend sit amet dui. Cras mattis pretium metus nec
venenatis. Nam tempus eros in tempus facilisis. Aliquam sit amet consequat
lacus. Mauris lacinia mollis justo eu dapibus. Maecenas vestibulum diam id mi
pellentesque accumsan.
</shared-card>

View File

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-tab-profile',
templateUrl: './tab-profile.component.html',
styleUrls: ['./tab-profile.component.scss']
})
export class TabProfileComponent {
}

View File

@ -0,0 +1,5 @@
<div class="mb-5">
<shared-table [items]="colonies" [columns]="columns"> </shared-table>
</div>
<shared-paginator total="20" perPage="5" />

View File

@ -0,0 +1,27 @@
import { Component } from "@angular/core";
import { ColumnDefinition } from "src/app/shared/components/table/table.component";
interface Colony {
name: string;
}
@Component({
selector: "app-tab-security",
templateUrl: "./tab-security.component.html",
styleUrls: ["./tab-security.component.scss"],
})
export class TabSecurityComponent {
columns: ColumnDefinition[] = [
{
header: "Name",
columnFunction: (colony: Colony) => colony.name,
routerLink: (colony: Colony) => "#",
},
{
header: "Name2",
columnFunction: (colony: Colony) => colony.name,
},
];
colonies: Colony[] = [{ name: "Die Römer" }, { name: "Die Griechen" }];
}

View File

@ -1,20 +1,27 @@
import { NgModule } from '@angular/core'; import { NgModule } from "@angular/core";
import { CommonModule } from '@angular/common'; import { CommonModule } from "@angular/common";
import { NavigationComponent } from './components/navigation/navigation.component'; import { NavigationComponent } from "./components/navigation/navigation.component";
import { AuthGuard } from './guards/auth.guard'; import { AuthGuard } from "./guards/auth.guard";
import { AuthService } from './services/auth.service'; import { AuthService } from "./services/auth.service";
import { RequestService } from './services/request.service'; import { RequestService } from "./services/request.service";
import { AppService } from './services/app.service'; import { AppService } from "./services/app.service";
import { SharedModule } from '../shared/shared.module'; import { SharedModule } from "../shared/shared.module";
import { SettingsComponent } from './components/settings/settings.component'; import { SettingsComponent } from "./components/settings/settings.component";
import { TabProfileComponent } from "./components/settings/tabs/tab-profile/tab-profile.component";
import { TabSecurityComponent } from "./components/settings/tabs/tab-security/tab-security.component";
import { RouterModule } from "@angular/router";
@NgModule({ @NgModule({
declarations: [NavigationComponent, SettingsComponent], declarations: [
exports: [NavigationComponent, SettingsComponent], NavigationComponent,
imports: [ SettingsComponent,
CommonModule, TabProfileComponent,
SharedModule TabSecurityComponent,
TabProfileComponent,
TabSecurityComponent,
], ],
providers: [AuthGuard, AuthService, RequestService, AppService] exports: [NavigationComponent, SettingsComponent],
imports: [CommonModule, SharedModule, RouterModule],
providers: [AuthGuard, AuthService, RequestService, AppService],
}) })
export class CoreModule { } export class CoreModule {}

View File

@ -1,26 +1,28 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from "@angular/router"; import {
ActivatedRouteSnapshot,
CanActivate,
Router,
RouterStateSnapshot,
UrlTree,
} from "@angular/router";
import { filter, map, Observable } from "rxjs"; import { filter, map, Observable } from "rxjs";
import { AuthService } from "../services/auth.service"; import { AuthService } from "../services/auth.service";
@Injectable() @Injectable()
export class AuthGuard implements CanActivate { export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
constructor(
private authService: AuthService,
private router: Router
) {
}
canActivate(): Observable<boolean> | boolean { canActivate(): Observable<boolean> | boolean {
return this.authService.currentState$.pipe( return this.authService.currentState$.pipe(
filter(currentState=>currentState!==undefined), filter((currentState) => currentState !== undefined),
map((currentState) => { map((currentState) => {
if (currentState === null) { if (currentState === null) {
this.router.navigateByUrl('/auth/login'); this.router.navigateByUrl("/auth/login");
return false; return false;
} }
return true; return true;
})); })
);
} }
} }

View File

@ -1,14 +1,14 @@
export interface ConfirmRegistrationRequest { export interface ConfirmRegistrationRequest {
id: string, id: string;
password: string, password: string;
passwordConfirmation: string, passwordConfirmation: string;
} }
export interface ConfirmRegistrationResponse { export interface ConfirmRegistrationResponse {
id: string, id: string;
username: string, username: string;
roleIdentifier: string, roleIdentifier: string;
createdAt: string, createdAt: string;
updatedAt: string, updatedAt: string;
permissions: string[] permissions: string[];
} }

View File

@ -1,8 +1,8 @@
export interface LoginRequest { export interface LoginRequest {
identifier: string, identifier: string;
password: string, password: string;
} }
export interface LoginResponse { export interface LoginResponse {
sessionId: string sessionId: string;
} }

View File

@ -1,7 +1,6 @@
export interface RegisterUserRequest { export interface RegisterUserRequest {
mail: string, mail: string;
username: string, username: string;
} }
export interface RegisterUserResponse { export interface RegisterUserResponse {}
}

View File

@ -1,12 +1,11 @@
export interface UserStateRequest { export interface UserStateRequest {}
}
export interface UserStateResponse { export interface UserStateResponse {
id: string, id: string;
sessionId: string, sessionId: string;
username: string, username: string;
roleIdentifier: string, roleIdentifier: string;
createdAt: string, createdAt: string;
updatedAt: string, updatedAt: string;
permissions: string[], permissions: string[];
} }

View File

@ -2,20 +2,20 @@ import { Injectable } from "@angular/core";
@Injectable() @Injectable()
export class AppService { export class AppService {
darkMode: boolean; darkMode: boolean;
constructor( constructor() {
) { this.darkMode =
this.darkMode = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches); window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches;
this.applyDarkMode(); this.applyDarkMode();
} }
private applyDarkMode(): void { private applyDarkMode(): void {
if (this.darkMode) { if (this.darkMode) {
document.documentElement.classList.add('theme-dark'); document.documentElement.classList.add("theme-dark");
} else { } else {
document.documentElement.classList.remove('theme-dark'); document.documentElement.classList.remove("theme-dark");
} }
} }

View File

@ -4,39 +4,42 @@ import { UserStateResponse } from "../models/user-state-request.model";
import { BehaviorSubject } from "rxjs"; import { BehaviorSubject } from "rxjs";
import { LoginRequest, LoginResponse } from "../models/login-request.model"; import { LoginRequest, LoginResponse } from "../models/login-request.model";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { RegisterUserRequest, RegisterUserResponse } from "../models/register-user-request.model"; import {
import { ConfirmRegistrationRequest, ConfirmRegistrationResponse } from "../models/confirm-registration-request.model"; RegisterUserRequest,
RegisterUserResponse,
} from "../models/register-user-request.model";
import {
ConfirmRegistrationRequest,
ConfirmRegistrationResponse,
} from "../models/confirm-registration-request.model";
@Injectable() @Injectable()
export class AuthService { export class AuthService {
currentState$ = new BehaviorSubject<UserStateResponse | null | undefined>(
undefined
);
currentState$ = new BehaviorSubject<UserStateResponse | null | undefined>(undefined); constructor(private requestService: RequestService, private router: Router) {}
constructor(
private requestService: RequestService,
private router: Router
) {
}
readUserState(): void { readUserState(): void {
this.requestService.get( this.requestService.get(
'user/state', "user/state",
{}, {},
(response: UserStateResponse) => { (response: UserStateResponse) => {
console.log('set next state'); console.log("set next state");
this.currentState$.next(response); this.currentState$.next(response);
}, },
() => { () => {
this.currentState$.next(null); this.currentState$.next(null);
} }
) );
} }
login(body: LoginRequest): LoginResponse|null { login(body: LoginRequest): LoginResponse | null {
let result = null; let result = null;
this.requestService.post( this.requestService.post(
'auth/login-user', "auth/login-user",
body, body,
(response: LoginResponse) => { (response: LoginResponse) => {
result = response; result = response;
@ -47,11 +50,11 @@ export class AuthService {
return result; return result;
} }
register(body: RegisterUserRequest): RegisterUserResponse|null { register(body: RegisterUserRequest): RegisterUserResponse | null {
let result = null; let result = null;
this.requestService.post( this.requestService.post(
'auth/register-user', "auth/register-user",
body, body,
(response: LoginResponse) => { (response: LoginResponse) => {
result = response; result = response;
@ -62,11 +65,13 @@ export class AuthService {
return result; return result;
} }
confirmRegistration(body: ConfirmRegistrationRequest): ConfirmRegistrationResponse|null { confirmRegistration(
body: ConfirmRegistrationRequest
): ConfirmRegistrationResponse | null {
let result = null; let result = null;
this.requestService.post( this.requestService.post(
'auth/confirm-registration', "auth/confirm-registration",
body, body,
(response: LoginResponse) => { (response: LoginResponse) => {
result = response; result = response;
@ -78,13 +83,9 @@ export class AuthService {
} }
logout(): void { logout(): void {
this.requestService.post( this.requestService.post("auth/logout-user", {}, (response: any) => {
'auth/logout-user',
{},
(response: any) => {
this.currentState$.next(undefined); this.currentState$.next(undefined);
this.readUserState(); this.readUserState();
} });
);
} }
} }

View File

@ -1,59 +1,63 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from "@angular/common/http";
import { Router } from '@angular/router'; import { Router } from "@angular/router";
import { Injectable } from '@angular/core'; import { Injectable } from "@angular/core";
@Injectable({ @Injectable({
providedIn: 'root' providedIn: "root",
}) })
export class RequestService { export class RequestService {
constructor(private http: HttpClient, private router: Router) {}
constructor( public post(
private http: HttpClient, apiPath: string,
private router: Router, body: any,
successFunction: Function,
errorFunction: Function | null = null
) { ) {
}
public post(apiPath: string, body: any, successFunction: Function, errorFunction: Function|null = null)
{
let url = this.obtainUrl(apiPath); let url = this.obtainUrl(apiPath);
let observable = this.http.post(url, body).subscribe( let observable = this.http.post(url, body).subscribe(
(answer:any) => { (answer: any) => {
successFunction(answer); successFunction(answer);
}, },
(error:any) => { (error: any) => {
if (errorFunction === null) if (errorFunction === null) this.handleError(error);
this.handleError(error); else errorFunction(error);
else
errorFunction(error);
} }
); );
} }
public get(apiPath: string, body: any, successFunction: Function, errorFunction: Function|null = null) public get(
{ apiPath: string,
body: any,
successFunction: Function,
errorFunction: Function | null = null
) {
let url = this.obtainUrl(apiPath); let url = this.obtainUrl(apiPath);
let observable = this.http.get(url, body).subscribe( let observable = this.http.get(url, body).subscribe(
(answer:any) => { (answer: any) => {
successFunction(answer); successFunction(answer);
}, },
(error:any) => { (error: any) => {
if (errorFunction === null) if (errorFunction === null) this.handleError(error);
this.handleError(error); else errorFunction(error);
else
errorFunction(error);
} }
); );
} }
public postFiles(apiPath: string, files: File[], successFunction: Function, errorFunction: Function|null = null) { public postFiles(
apiPath: string,
files: File[],
successFunction: Function,
errorFunction: Function | null = null
) {
if (!files || files.length === 0) { if (!files || files.length === 0) {
throw 'Need to select at least one file'; throw "Need to select at least one file";
} }
const formData = new FormData(); const formData = new FormData();
for (let fileIndex = 0; fileIndex < files.length; fileIndex++) { for (let fileIndex = 0; fileIndex < files.length; fileIndex++) {
formData.append('file' + fileIndex, files[fileIndex]); formData.append("file" + fileIndex, files[fileIndex]);
} }
let url = this.obtainUrl(apiPath); let url = this.obtainUrl(apiPath);
@ -62,10 +66,8 @@ export class RequestService {
successFunction(answer); successFunction(answer);
}, },
(error: any) => { (error: any) => {
if (errorFunction === null) if (errorFunction === null) this.handleError(error);
this.handleError(error); else errorFunction(error);
else
errorFunction(error);
} }
); );
} }
@ -73,34 +75,33 @@ export class RequestService {
private obtainUrl(apiPath: string): string { private obtainUrl(apiPath: string): string {
let hostString = window.location.host; let hostString = window.location.host;
let protocol = window.location.protocol; let protocol = window.location.protocol;
return protocol + '//' + hostString + '/api/' + apiPath; return protocol + "//" + hostString + "/api/" + apiPath;
} }
private handleError(answer: any): void { private handleError(answer: any): void {
if (answer.status == 401) { if (answer.status == 401) {
console.log('Deine Sitzung konnte nicht gefunden werden'); console.log("Deine Sitzung konnte nicht gefunden werden");
this.router.navigate(['/auth']); this.router.navigate(["/auth"]);
return; return;
} }
if (answer.status == 403) { if (answer.status == 403) {
this.showSnackBar('Du bist nicht für diese Aktion autorisiert', 'Ok'); this.showSnackBar("Du bist nicht für diese Aktion autorisiert", "Ok");
return; return;
} }
try { try {
let errorObject = answer.error.error; let errorObject = answer.error.error;
if (errorObject.hasOwnProperty('message')) { if (errorObject.hasOwnProperty("message")) {
throw errorObject.message.toString(); throw errorObject.message.toString();
} }
if (errorObject.hasOwnProperty('code')) { if (errorObject.hasOwnProperty("code")) {
throw errorObject.code.toString(); throw errorObject.code.toString();
} }
} catch(error:any) { } catch (error: any) {
this.showSnackBar(error.toString(), 'Ok'); this.showSnackBar(error.toString(), "Ok");
} }
} }

View File

@ -2,7 +2,11 @@
id="card" id="card"
class="grow p-3 my-2 rounded-xl shadow-sm shadow-skin-primary border border-skin-primary divide-y divide-skin-primary" class="grow p-3 my-2 rounded-xl shadow-sm shadow-skin-primary border border-skin-primary divide-y divide-skin-primary"
> >
<div *ngIf="icon !== null || header !== null || subHeader !== null" id="header" class="mb-5"> <div
*ngIf="icon !== null || header !== null || subHeader !== null"
id="header"
class="mb-5"
>
<div class="w-full flex flex-row"> <div class="w-full flex flex-row">
<!--Icon--> <!--Icon-->
<div *ngIf="icon !== null" class="basis-auto"> <div *ngIf="icon !== null" class="basis-auto">
@ -13,12 +17,11 @@
<div class="basis-full truncate"> <div class="basis-full truncate">
<div <div
*ngIf="header !== null" *ngIf="header !== null"
class="text-xl font-medium text-skin-primary mb-2"> class="text-xl font-medium text-skin-primary mb-2"
>
{{ header }} {{ header }}
</div> </div>
<div <div *ngIf="subHeader !== null" class="text-skin-primary-muted pl-2">
*ngIf="subHeader !== null"
class="text-skin-primary-muted pl-2">
{{ subHeader }} {{ subHeader }}
</div> </div>
</div> </div>

View File

@ -1,9 +1,9 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from "@angular/core";
@Component({ @Component({
selector: 'shared-card', selector: "shared-card",
templateUrl: './card.component.html', templateUrl: "./card.component.html",
styleUrls: ['./card.component.scss'], styleUrls: ["./card.component.scss"],
}) })
export class CardComponent { export class CardComponent {
@Input() header: string | null = null; @Input() header: string | null = null;

View File

@ -28,9 +28,9 @@
<div <div
class="flex items-center justify-center h-8 leading-tight border border-skin-primary text-skin-primary" class="flex items-center justify-center h-8 leading-tight border border-skin-primary text-skin-primary"
[ngClass]="{ [ngClass]="{
'bg-skin-accent font-bold text-skin-secondary hover:text-skin-primary': item.page === page, 'bg-skin-accent font-bold text-skin-secondary hover:text-skin-primary':
'bg-skin-secondary': item.page === page,
item.page !== page 'bg-skin-secondary': item.page !== page
}" }"
> >
<div *ngIf="item.page === null" class="px-3">...</div> <div *ngIf="item.page === null" class="px-3">...</div>

View File

@ -5,16 +5,16 @@ import {
OnChanges, OnChanges,
Output, Output,
SimpleChanges, SimpleChanges,
} from '@angular/core'; } from "@angular/core";
export interface PaginatorItem { export interface PaginatorItem {
page: number | null; page: number | null;
} }
@Component({ @Component({
selector: 'shared-paginator', selector: "shared-paginator",
templateUrl: './paginator.component.html', templateUrl: "./paginator.component.html",
styleUrls: ['./paginator.component.scss'], styleUrls: ["./paginator.component.scss"],
}) })
export class PaginatorComponent implements OnChanges { export class PaginatorComponent implements OnChanges {
@Output() pageChange = new EventEmitter<number>(); @Output() pageChange = new EventEmitter<number>();

View File

@ -1,4 +1,4 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from "@angular/core";
export interface ColumnDefinition { export interface ColumnDefinition {
header: string; header: string;
@ -8,9 +8,9 @@ export interface ColumnDefinition {
} }
@Component({ @Component({
selector: 'shared-table', selector: "shared-table",
templateUrl: './table.component.html', templateUrl: "./table.component.html",
styleUrls: ['./table.component.scss'], styleUrls: ["./table.component.scss"],
}) })
export class TableComponent { export class TableComponent {
@Input() items: any[] = []; @Input() items: any[] = [];
@ -18,6 +18,6 @@ export class TableComponent {
isBoolean(obje: any): boolean { isBoolean(obje: any): boolean {
console.log(obje); console.log(obje);
return typeof obje === 'boolean'; return typeof obje === "boolean";
} }
} }

View File

@ -1,4 +0,0 @@
export interface TabItem {
id: string;
title: string;
}

View File

@ -0,0 +1,4 @@
export interface TabItem {
id: string;
title: string;
}

View File

@ -1,28 +1,14 @@
import { NgModule } from '@angular/core'; import { NgModule } from "@angular/core";
import { RouterModule } from '@angular/router'; import { RouterModule } from "@angular/router";
import { CommonModule } from '@angular/common'; import { CommonModule } from "@angular/common";
import { CardComponent } from './components/card/card.component'; import { CardComponent } from "./components/card/card.component";
import { PaginatorComponent } from './components/paginator/paginator.component'; import { PaginatorComponent } from "./components/paginator/paginator.component";
import { TableComponent } from './components/table/table.component'; import { TableComponent } from "./components/table/table.component";
import { FormsModule } from '@angular/forms'; import { FormsModule } from "@angular/forms";
@NgModule({ @NgModule({
declarations: [ declarations: [CardComponent, PaginatorComponent, TableComponent],
CardComponent, exports: [CardComponent, PaginatorComponent, TableComponent, FormsModule],
PaginatorComponent, imports: [RouterModule, CommonModule, FormsModule],
TableComponent,
],
exports: [
CardComponent,
PaginatorComponent,
TableComponent,
FormsModule,
],
imports: [
RouterModule,
CommonModule,
FormsModule,
],
}) })
export class SharedModule {} export class SharedModule {}

View File

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

View File

@ -4,10 +4,10 @@
@layer base { @layer base {
:root { :root {
--color-text-primary: 0,0,0; --color-text-primary: 0, 0, 0;
--color-text-primary-muted: 100,100,100; --color-text-primary-muted: 100, 100, 100;
--color-text-secondary: 255,255,255; --color-text-secondary: 255, 255, 255;
--color-text-secondary-muted: 170,170,170; --color-text-secondary-muted: 170, 170, 170;
--color-text-accent: 255, 199, 44; --color-text-accent: 255, 199, 44;
--color-text-accent-muted: 255, 199, 44; --color-text-accent-muted: 255, 199, 44;
@ -16,10 +16,10 @@
--color-accent: 255, 199, 44; --color-accent: 255, 199, 44;
} }
.theme-dark { .theme-dark {
--color-text-primary: 255,255,255; --color-text-primary: 255, 255, 255;
--color-text-primary-muted: 170,170,170; --color-text-primary-muted: 170, 170, 170;
--color-text-secondary: 0,0,0; --color-text-secondary: 0, 0, 0;
--color-text-secondary-muted: 100,100,100; --color-text-secondary-muted: 100, 100, 100;
--color-text-accent: 250, 183, 0; --color-text-accent: 250, 183, 0;
--color-text-accent-muted: 250, 183, 0; --color-text-accent-muted: 250, 183, 0;
@ -27,5 +27,4 @@
--color-secondary: 39, 39, 42; --color-secondary: 39, 39, 42;
--color-accent: 250, 183, 0; --color-accent: 250, 183, 0;
} }
} }

View File

@ -1,61 +1,56 @@
function withOpacity(variableName) { function withOpacity(variableName) {
return ({ opacityValue }) => { return ({ opacityValue }) => {
if (opacityValue !== undefined) { if (opacityValue !== undefined) {
return `rgba(var(${variableName}), ${opacityValue})` return `rgba(var(${variableName}), ${opacityValue})`;
}
return `rgb(var(${variableName}))`
} }
return `rgb(var(${variableName}))`;
};
} }
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
module.exports = { module.exports = {
content: [ content: ["./src/**/*.{html,ts}", "./node_modules/flowbite/**/*.js"],
"./src/**/*.{html,ts}",
"./node_modules/flowbite/**/*.js"
],
theme: { theme: {
extend: { extend: {
textColor: { textColor: {
skin: { skin: {
primary: withOpacity('--color-text-primary'), primary: withOpacity("--color-text-primary"),
'primary-muted': withOpacity('--color-text-primary-muted'), "primary-muted": withOpacity("--color-text-primary-muted"),
secondary: withOpacity('--color-text-secondary'), secondary: withOpacity("--color-text-secondary"),
'secondary-muted': withOpacity('--color-text-secondary-muted'), "secondary-muted": withOpacity("--color-text-secondary-muted"),
accent: withOpacity('--color-text-accent'), accent: withOpacity("--color-text-accent"),
'accent-muted': withOpacity('--color-text-accent-muted'), "accent-muted": withOpacity("--color-text-accent-muted"),
}, },
}, },
backgroundColor: { backgroundColor: {
skin: { skin: {
primary: withOpacity('--color-primary'), primary: withOpacity("--color-primary"),
secondary: withOpacity('--color-secondary'), secondary: withOpacity("--color-secondary"),
accent: withOpacity('--color-accent'), accent: withOpacity("--color-accent"),
}, },
}, },
boxShadowColor: { boxShadowColor: {
skin: { skin: {
primary: withOpacity('--color-text-secondary-muted'), primary: withOpacity("--color-text-secondary-muted"),
} },
}, },
ringColor: { ringColor: {
skin: { skin: {
primary: withOpacity('--color-text-secondary-muted'), primary: withOpacity("--color-text-secondary-muted"),
} },
}, },
divideColor: { divideColor: {
skin: { skin: {
primary: withOpacity('--color-text-secondary-muted'), primary: withOpacity("--color-text-secondary-muted"),
} },
}, },
borderColor: { borderColor: {
skin: { skin: {
primary: withOpacity('--color-text-secondary-muted'), primary: withOpacity("--color-text-secondary-muted"),
} accent: withOpacity("--color-text-accent-muted"),
}
}, },
}, },
plugins: [ },
require('flowbite/plugin') },
], plugins: [require("flowbite/plugin")],
} };