Compare commits

..

No commits in common. "d8d09d7bac31fe1c7456ed481b37e004f9f7c28d" and "070e9bb87649aea011e0e016fc10491d8a02a0d6" have entirely different histories.

31 changed files with 200 additions and 579 deletions

View File

@ -84,10 +84,10 @@
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "bee:build:production"
"browserTarget": "bee:build:production"
},
"development": {
"buildTarget": "bee:build:development"
"browserTarget": "bee:build:development"
}
},
"defaultConfiguration": "development"
@ -95,7 +95,7 @@
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "bee:build"
"browserTarget": "bee:build"
}
},
"test": {

View File

@ -9,25 +9,24 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^18.2.2",
"@angular/common": "^18.2.2",
"@angular/compiler": "^18.2.2",
"@angular/core": "^18.2.2",
"@angular/forms": "^18.2.2",
"@angular/platform-browser": "^18.2.2",
"@angular/platform-browser-dynamic": "^18.2.2",
"@angular/router": "^18.2.2",
"@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0",
"@angular/animations": "^15.2.10",
"@angular/cdk": "^15.2.9",
"@angular/common": "^15.2.0",
"@angular/compiler": "^15.2.0",
"@angular/core": "^15.2.0",
"@angular/forms": "^15.2.0",
"@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.14.10"
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^18.2.2",
"@angular/cli": "~18.2.2",
"@angular/compiler-cli": "^18.2.2",
"@angular-devkit/build-angular": "^15.2.6",
"@angular/cli": "~15.2.6",
"@angular/compiler-cli": "^15.2.0",
"@types/jasmine": "~4.3.0",
"jasmine-core": "~4.5.0",
"karma": "~6.4.0",
@ -36,6 +35,6 @@
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.0.0",
"tailwindcss": "^3.4.10",
"typescript": "~5.4.5"
"typescript": "~4.9.4"
}
}

View File

@ -1,5 +1 @@
<router-outlet></router-outlet>
<div class="absolute bottom-0 w-full z-50">
<app-notification-bar />
</div>

View File

@ -1,7 +1,6 @@
import { Component, OnInit } from "@angular/core";
import { AuthService } from "./core/services/auth.service";
import { AppService } from "./core/services/app.service";
import { TranslateService } from "@ngx-translate/core";
@Component({
selector: "app-root",
@ -11,13 +10,8 @@ import { TranslateService } from "@ngx-translate/core";
export class AppComponent implements OnInit {
constructor(
private authService: AuthService,
private translate: TranslateService
) {
var language = navigator.language;
translate.setDefaultLang(language);
translate.use(language);
}
private appService: AppService
) {}
ngOnInit(): void {
this.authService.readUserState();

View File

@ -1,13 +1,8 @@
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import {
HttpClient,
provideHttpClient,
withInterceptorsFromDi,
} from "@angular/common/http";
import { HttpClientModule } from "@angular/common/http";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { TranslateModule, TranslateLoader } from "@ngx-translate/core";
import { TranslateHttpLoader } from "@ngx-translate/http-loader";
import { AppComponent } from "./app.component";
import { RouterModule, Routes } from "@angular/router";
import { SharedModule } from "./shared/shared.module";
@ -30,28 +25,18 @@ const routes: Routes = [
},
];
export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, "./assets/i18n/", ".json");
}
@NgModule({
declarations: [AppComponent, HomeComponent],
bootstrap: [AppComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
RouterModule,
RouterModule.forRoot(routes),
SharedModule,
CoreModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: createTranslateLoader,
deps: [HttpClient],
},
}),
],
providers: [provideHttpClient(withInterceptorsFromDi())],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}

View File

@ -7,7 +7,6 @@ import { RouterModule, Routes } from "@angular/router";
import { ReactiveFormsModule } from "@angular/forms";
import { ForgotPasswordComponent } from "./components/forgot-password/forgot-password.component";
import { ResetPasswordComponent } from "./components/reset-password/reset-password.component";
import { TranslateModule } from "@ngx-translate/core";
const routes: Routes = [
{ path: "login", component: LoginComponent },
@ -39,11 +38,6 @@ const routes: Routes = [
ForgotPasswordComponent,
ResetPasswordComponent,
],
imports: [
RouterModule.forChild(routes),
CommonModule,
ReactiveFormsModule,
TranslateModule,
],
imports: [RouterModule.forChild(routes), CommonModule, ReactiveFormsModule],
})
export class AuthModule {}

View File

@ -2,10 +2,10 @@
<div class="max-w-sm mx-auto mb-10">
<h1 class="font-bold text-center text-skin-primary text-5xl mb-5">
{{ "title" | translate }}
Beekeeper
</h1>
<h1 class="font-bold text-center text-skin-accent text-xl">
{{ "auth.confirm-registration" | translate }}
Registrierung Abschließen
</h1>
</div>
@ -14,7 +14,7 @@
<label
for="password"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>{{ "auth.password" | translate }}</label
>Passwort</label
>
<input
formControlName="password"
@ -28,7 +28,7 @@
<label
for="passwordConfirmation"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>{{ "auth.password-confirmation" | translate }}</label
>Passwort wiederholen</label
>
<input
formControlName="passwordConfirmation"
@ -44,6 +44,6 @@
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"
>
{{ "auth.confirm-registration" | translate }}
Registrieren
</button>
</form>

View File

@ -2,10 +2,10 @@
<div class="max-w-sm mx-auto mb-10">
<h1 class="font-bold text-center text-skin-primary text-5xl mb-5">
{{ "title" | translate }}
Beekeeper
</h1>
<h1 class="font-bold text-center text-skin-accent text-xl">
{{ "auth.forgot-password" | translate }}
Passwort vergessen?
</h1>
</div>
@ -14,8 +14,7 @@
<label
for="mail"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>
{{ "auth.email" | translate }}</label
>E-Mail</label
>
<input
formControlName="mail"
@ -31,6 +30,6 @@
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"
>
{{ "auth.reset-password" | translate }}
Passwort zurücksetzen
</button>
</form>

View File

@ -2,11 +2,9 @@
<div class="max-w-sm mx-auto mb-10">
<h1 class="font-bold text-center text-skin-primary text-5xl mb-5">
{{ "title" | translate }}
</h1>
<h1 class="font-bold text-center text-skin-accent text-xl">
{{ "auth.login" | translate }}
Beekeeper
</h1>
<h1 class="font-bold text-center text-skin-accent text-xl">Anmeldung</h1>
</div>
<form class="max-w-sm mx-auto" [formGroup]="loginForm">
@ -14,7 +12,7 @@
<label
for="identifier"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>{{ "auth.username-or-email" | translate }}</label
>Benutzername oder E-Mail</label
>
<input
formControlName="identifier"
@ -28,11 +26,11 @@
id="helper-text-explanation"
class="mt-2 text-sm text-skin-primary-muted"
>
{{ "auth.not-yet-registered" | translate }}
Neu hier?
<a
routerLink="/auth/registration"
class="font-medium text-skin-accent hover:underline hover:font-bold"
>{{ "auth.register-now" | translate }}</a
>Jetzt registrieren!</a
>
</p>
</div>
@ -40,7 +38,7 @@
<label
for="password"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>{{ "auth.password" | translate }}</label
>Passwort</label
>
<input
formControlName="password"
@ -56,7 +54,7 @@
<a
routerLink="/auth/forgot-password"
class="font-medium text-skin-accent hover:underline hover:font-bold"
>{{ "auth.forgot-password" | translate }}?</a
>Passwort vergessen?</a
>
</p>
</div>
@ -66,6 +64,6 @@
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"
>
{{ "auth.login" | translate }}
Anmelden
</button>
</form>

View File

@ -2,11 +2,9 @@
<div class="max-w-sm mx-auto mb-10">
<h1 class="font-bold text-center text-skin-primary text-5xl mb-5">
{{ "title" | translate }}
</h1>
<h1 class="font-bold text-center text-skin-accent text-xl">
{{ "auth.registration" | translate }}
Beekeeper
</h1>
<h1 class="font-bold text-center text-skin-accent text-xl">Registrierung</h1>
</div>
<form class="max-w-sm mx-auto" [formGroup]="registrationForm">
@ -14,8 +12,7 @@
<label
for="mail"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>
{{ "auth.email" | translate }}</label
>E-Mail</label
>
<input
formControlName="mail"
@ -30,8 +27,7 @@
<label
for="username"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>
{{ "auth.reset-password" | translate }}</label
>Benutzername</label
>
<input
formControlName="username"
@ -44,11 +40,11 @@
id="helper-text-explanation"
class="mt-2 text-sm text-skin-primary-muted"
>
{{ "auth.already-registered" | translate }}
Bereits registiert?
<a
routerLink="/auth/login"
class="font-medium text-skin-accent hover:underline hover:font-bold"
>{{ "auth.login-now" | translate }}</a
>Jetzt anmelden!</a
>
</p>
</div>
@ -58,6 +54,6 @@
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"
>
{{ "auth.register" | translate }}
Registrieren
</button>
</form>

View File

@ -2,10 +2,10 @@
<div class="max-w-sm mx-auto mb-10">
<h1 class="font-bold text-center text-skin-primary text-5xl mb-5">
{{ "title" | translate }}
Beekeeper
</h1>
<h1 class="font-bold text-center text-skin-accent text-xl">
{{ "auth.reset-password" | translate }}
Passwort zurücksetzen
</h1>
</div>
@ -14,7 +14,7 @@
<label
for="newPassword"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>{{ "auth.new-password" | translate }}</label
>Neues Passwort</label
>
<input
formControlName="newPassword"
@ -28,7 +28,7 @@
<label
for="passwordConfirmation"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>{{ "auth.password-confirmation" | translate }}</label
>Passwort wiederholen</label
>
<input
formControlName="passwordConfirmation"
@ -44,6 +44,6 @@
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"
>
{{ "auth.new-password" | translate }}
Neues Passwort bestätigen
</button>
</form>

View File

@ -1,10 +1,5 @@
<app-navigation></app-navigation>
<div class="mx-auto p-5 flex flex-row">
<div id="sidebar-placehodler" class="basis-auto">
<div class="w-0 xl:w-64"></div>
</div>
<div id="content" class="basis-full">
<div class="max-w-screen-xl mx-auto p-4">
<router-outlet></router-outlet>
</div>
</div>

View File

@ -1,38 +1,21 @@
<nav class="bg-skin-accent">
<div class="h-20 flex flex-wrap items-center justify-between mx-auto p-4">
<!--Burger Menu-->
<button
data-collapse-toggle="default-sidebar"
type="button"
class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-skin-primary rounded-lg hover:bg-skin-primary xl:hidden"
aria-controls="default-sidebar"
aria-expanded="false"
<div
class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4"
>
<span class="sr-only">Open main menu</span>
<svg class="w-5 h-5" aria-hidden="true" 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>
</button>
<!-- Title -->
<a routerLink="/" class="flex items-center space-x-3">
<img src="assets/icon.png" class="h-10 w-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"
>{{ "title" | translate }}</span
>Bienenkeeper</span
>
</a>
<div class="flex items-center md:order-2 space-x-3 md:space-x-0">
<!--User Bubble-->
<button
type="button"
class="w-12 h-12 text-sm bg-skin-primary rounded-full focus:ring-4 focus:ring-skin-primary"
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"
@ -42,30 +25,36 @@
state?.username[0] + state?.roleIdentifier[0]
}}</span>
</button>
</div>
</nav>
<!-- Navbar -->
<div
id="default-sidebar"
class="fixed hidden xl:block -bottom-20 left-0 z-40 w-64 h-screen translate-x-0 bg-skin-secondary"
aria-label="Sidebar"
<!--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"
>
<ul class="text-xl p-4 rounded-lg">
<li *ngFor="let navigationLink of navigationLinks">
<a
class="block py-2 px-3 rounded text-skin-primary hover:text-skin-secondary hover:bg-skin-accent"
[routerLink]="navigationLink.routerLink"
[routerLinkActiveOptions]="{ exact: true }"
routerLinkActive="font-bold"
>{{ getNavigationLinkLabel(navigationLink) | translate }}</a
<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"
>
</li>
</ul>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M1 1h15M1 7h15M1 13h15"
/>
</svg>
</button>
</div>
<!-- User dropdown -->
<div id="user-dropdown" class="hidden min-w-full md:min-w-64 px-4 z-50">
<div id="user-dropdown" class="hidden min-w-full md:min-w-72 px-4">
<div
class="text-base list-none divide-y divide-skin-primary rounded-lg shadow-sm shadow-skin-primary bg-skin-primary"
>
@ -81,17 +70,39 @@
<ul class="p-4" aria-labelledby="user-menu-button">
<li *ngFor="let usermenuButton of usermenuButtons">
<button
class="w-full text-center block py-2 px-3 rounded text-skin-primary hover:bg-skin-accent"
[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) | translate }}
{{ getUserMenuButtonLabel(usermenuButton) }}
</button>
</li>
</ul>
</div>
</div>
<!-- Navigatoin -->
<div
class="items-center justify-between hidden w-full md:flex md:w-auto md:order-1"
id="navbar-user"
>
<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"
>
<li *ngFor="let navigationLink of navigationLinks">
<a
class="block py-2 px-3 rounded text-skin-primary hover:text-skin-secondary hover:bg-skin-accent md:p-0"
[routerLink]="navigationLink.routerLink"
[routerLinkActiveOptions]="{ exact: true }"
routerLinkActive="font-bold"
>{{ navigationLink.label }}</a
>
</li>
</ul>
</div>
</div>
</nav>

View File

@ -3,13 +3,11 @@ 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 } from "rxjs";
import { filter, map } from "rxjs";
import { AppService } from "../../services/app.service";
import { NotificationService } from "../../services/notification.service";
import { TranslateService } from "@ngx-translate/core";
interface NavigationLink {
label: Function | string;
label: string;
routerLink: string;
}
@ -26,45 +24,25 @@ interface UserMenuButton {
})
export class NavigationComponent implements OnInit {
navigationLinks: NavigationLink[] = [
{ routerLink: "/", label: () => "navigation.home" },
{
routerLink: "/colonies",
label: () => "navigation.colonies",
},
{ routerLink: "/", label: "Startseite" },
{ routerLink: "/settings", label: "Völker" },
];
usermenuButtons: UserMenuButton[] = [
{
label: () => "menu.settings",
label: "Einstellungen",
routerLink: "/settings",
clickCallback: undefined,
},
{
label: () => (this.darkMode ? "menu.theme.light" : "menu.theme.dark"),
label: () => (this.darkMode ? "Hell" : "Dunkel"),
routerLink: undefined,
clickCallback: () => {
this.toggleDarkmode();
},
},
{
label: () => (this.language === "de" ? "languages.en" : "languages.de"),
routerLink: undefined,
clickCallback: () => {
this.toggleLanguage();
},
},
{
label: "Test",
routerLink: undefined,
clickCallback: () => {
this.notificationService.push(
this.translate.instant("error.Generic.SomethingWentWrong"),
"danger"
);
},
},
{
label: () => "menu.logout",
label: "Ausloggen",
routerLink: undefined,
clickCallback: () => {
this.logout();
@ -74,28 +52,13 @@ export class NavigationComponent implements OnInit {
state: UserStateResponse | undefined | null;
darkMode: boolean;
language: "de" | "en";
constructor(
private authService: AuthService,
private appService: AppService,
private notificationService: NotificationService,
private translate: TranslateService,
private router: Router
) {
this.darkMode = this.appService.darkMode;
switch (translate.currentLang) {
case "en":
this.language = "en";
break;
case "de":
this.language = "de";
break;
default:
this.language = "de";
}
this.state = this.authService.currentState$.value;
this.authService.currentState$
.pipe(filter((state) => state === undefined))
@ -117,19 +80,6 @@ export class NavigationComponent implements OnInit {
this.darkMode = this.appService.darkMode;
}
toggleLanguage() {
switch (this.language) {
case "en":
this.language = "de";
break;
case "de":
this.language = "en";
break;
}
this.translate.use(this.language);
}
clickUserMenuButtonLabel(button: UserMenuButton) {
if (button.clickCallback !== undefined) {
button.clickCallback();
@ -143,11 +93,4 @@ export class NavigationComponent implements OnInit {
return button.label;
}
}
getNavigationLinkLabel(link: NavigationLink) {
if (typeof link.label === "function") {
return link.label();
} else {
return link.label;
}
}
}

View File

@ -1,42 +0,0 @@
<div
*ngFor="let item of items"
class="flex items-center opacity-90 p-4 mb-4 text-skin-accent rounded-lg bg-skin-secondary md:max-w-3xl mx-5 md:mx-auto shadow-sm shadow-skin-primary"
>
<svg
class="flex-shrink-0 w-4 h-4"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"
/>
</svg>
<span class="sr-only">{{ item.element.type }}</span>
<div class="ms-3 text-sm font-medium">
{{ item.element.message }}
</div>
<button
type="button"
class="ms-auto -mx-1.5 -my-1.5 bg-skin-secondary text-skin-accent rounded-lg focus:ring-2 focus:ring-blue-400 p-1.5 hover:bg-skin-primary inline-flex items-center justify-center h-8 w-8"
(click)="dismiss(item)"
>
<span class="sr-only">Close</span>
<svg
class="w-3 h-3"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 14 14"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"
/>
</svg>
</button>
</div>

View File

@ -1,69 +0,0 @@
import { Component } from "@angular/core";
import { NotificationService } from "../../services/notification.service";
export interface NotificationElement {
message: string;
type: "info" | "danger" | "warn" | "success";
timeout: number | undefined;
}
interface NotificationItem {
element: NotificationElement;
shownAt: Date;
}
@Component({
selector: "app-notification-bar",
templateUrl: "./notification-bar.component.html",
styleUrls: ["./notification-bar.component.scss"],
})
export class NotificationBarComponent {
items: NotificationItem[] = [];
isRunning: boolean;
constructor(private notificationService: NotificationService) {
this.isRunning = false;
this.notificationService.notification$.subscribe((notification) => {
if (notification !== undefined) {
this.items.push({
element: notification,
shownAt: new Date(),
});
this.startTimeoutIfNecessary();
}
});
}
startTimeoutIfNecessary(): void {
if (this.isRunning === true) {
return;
}
let intervalId = setInterval(() => {
this.isRunning = true;
let now = new Date();
this.items.forEach((item) => {
if (item.element.timeout !== undefined) {
let timeout = new Date(
item.shownAt.getTime() + item.element.timeout * 1000
);
if (timeout < now) {
this.dismiss(item);
}
}
});
if (this.items.length === 0) {
clearInterval(intervalId);
this.isRunning = false;
}
}, 1000);
}
dismiss(item: NotificationItem): void {
this.items = this.items.filter((i) => i !== item);
}
}

View File

@ -27,7 +27,7 @@
aria-controls="profile"
aria-selected="false"
>
{{ "settings.tab-profile" | translate }}
Profil
</button>
</li>
<li class="me-2" role="presentation">
@ -40,7 +40,7 @@
aria-controls="security"
aria-selected="false"
>
{{ "settings.tab-security" | translate }}
Sicherheit
</button>
</li>
</ul>

View File

@ -1,14 +1,10 @@
import { Component } from "@angular/core";
interface User {
username: string;
mail: string;
age: number;
}
import { Component } from '@angular/core';
@Component({
selector: "app-tab-profile",
templateUrl: "./tab-profile.component.html",
styleUrls: ["./tab-profile.component.scss"],
selector: 'app-tab-profile',
templateUrl: './tab-profile.component.html',
styleUrls: ['./tab-profile.component.scss']
})
export class TabProfileComponent {}
export class TabProfileComponent {
}

View File

@ -1,12 +1,12 @@
<div class="grid grid-cols-1 md:grid-cols-2">
<div>
<h2>{{ "settings.security.change-password" | translate }}</h2>
<h2>Passwort ändern</h2>
<form class="mx-auto p-5" [formGroup]="changePasswordForm">
<div class="mb-5">
<label
for="password"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>{{ "settings.security.current-password" | translate }}</label
>Aktuelles Passwort</label
>
<input
formControlName="password"
@ -21,7 +21,7 @@
<label
for="newPassword"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>{{ "settings.security.new-password" | translate }}</label
>Neues Passwort</label
>
<input
formControlName="newPassword"
@ -36,7 +36,7 @@
<label
for="newPasswordConfirmation"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>{{ "settings.security.password-confirmation" | translate }}</label
>Neues Passwort bestätigen</label
>
<input
formControlName="newPasswordConfirmation"
@ -53,19 +53,19 @@
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"
>
{{ "settings.security.change-password" | translate }}
Passwort ändern
</button>
</form>
</div>
<div>
<h2>{{ "settings.security.change-username" | translate }}</h2>
<h2>Benutername ändern</h2>
<form class="mx-auto p-5" [formGroup]="changeUsernameForm">
<div class="mb-5">
<label
for="password"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>{{ "settings.security.current-password" | translate }}</label
>Aktuelles Passwort</label
>
<input
formControlName="password"
@ -80,7 +80,7 @@
<label
for="newUsername"
class="block mb-2 text-sm font-medium text-skin-primary-muted"
>{{ "settings.security.new-username" | translate }}</label
>Neuer Benutzername</label
>
<input
formControlName="newUsername"
@ -97,7 +97,7 @@
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"
>
{{ "settings.security.change-username" | translate }}
Benutzername ändern
</button>
</form>
</div>

View File

@ -11,9 +11,6 @@ import { TabProfileComponent } from "./components/settings/tabs/tab-profile/tab-
import { TabSecurityComponent } from "./components/settings/tabs/tab-security/tab-security.component";
import { RouterModule } from "@angular/router";
import { ReactiveFormsModule } from "@angular/forms";
import { NotificationBarComponent } from "./components/notification-bar/notification-bar.component";
import { NotificationService } from "./services/notification.service";
import { TranslateModule } from "@ngx-translate/core";
@NgModule({
declarations: [
@ -21,28 +18,14 @@ import { TranslateModule } from "@ngx-translate/core";
SettingsComponent,
TabProfileComponent,
TabSecurityComponent,
NotificationBarComponent,
],
exports: [
NavigationComponent,
SettingsComponent,
TabProfileComponent,
TabSecurityComponent,
NotificationBarComponent,
],
imports: [
CommonModule,
SharedModule,
RouterModule,
ReactiveFormsModule,
TranslateModule,
],
providers: [
AuthGuard,
AuthService,
RequestService,
AppService,
NotificationService,
],
imports: [CommonModule, SharedModule, RouterModule, ReactiveFormsModule],
providers: [AuthGuard, AuthService, RequestService, AppService],
})
export class CoreModule {}

View File

@ -1,10 +1,16 @@
import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot, UrlTree } from "@angular/router";
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 {
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(): Observable<boolean> | boolean {

View File

@ -42,6 +42,7 @@ export class AuthService {
"user/state",
{},
(response: UserStateResponse) => {
console.log("set next state");
this.currentState$.next(response);
},
() => {

View File

@ -1,34 +0,0 @@
import { Injectable } from "@angular/core";
import { BehaviorSubject, Observable, Subject } from "rxjs";
import { NotificationElement } from "../components/notification-bar/notification-bar.component";
@Injectable()
export class NotificationService {
notification$ = new Subject<NotificationElement>();
push(message: string, type: "info" | "danger" | "warn" | "success"): void {
console.log(type + ": " + message);
let timeout = undefined;
switch (type) {
case "info":
timeout = 5;
break;
case "danger":
timeout = 10;
break;
case "warn":
timeout = 7;
break;
case "success":
timeout = 3;
break;
}
this.notification$.next({
message: message,
type: type,
timeout: timeout,
});
}
}

View File

@ -1,19 +1,12 @@
import { HttpClient } from "@angular/common/http";
import { Router } from "@angular/router";
import { Injectable } from "@angular/core";
import { NotificationService } from "./notification.service";
import { TranslateService } from "@ngx-translate/core";
@Injectable({
providedIn: "root",
})
export class RequestService {
constructor(
private http: HttpClient,
private router: Router,
private notificationService: NotificationService,
private translate: TranslateService
) {}
constructor(private http: HttpClient, private router: Router) {}
public post(
apiPath: string,
@ -87,13 +80,13 @@ export class RequestService {
private handleError(answer: any): void {
if (answer.status == 401) {
this.showSnackBar("Deine Sitzung konnte nicht gefunden werden");
console.log("Deine Sitzung konnte nicht gefunden werden");
this.router.navigate(["/auth"]);
return;
}
if (answer.status == 403) {
this.showSnackBar("Du bist nicht für diese Aktion autorisiert");
this.showSnackBar("Du bist nicht für diese Aktion autorisiert", "Ok");
return;
}
@ -105,17 +98,17 @@ export class RequestService {
}
if (errorObject.hasOwnProperty("code")) {
throw (
this.translate.instant("error." + errorObject.code.toString()) ??
errorObject.code.toString()
);
throw errorObject.code.toString();
}
} catch (error: any) {
this.showSnackBar(error.toString());
this.showSnackBar(error.toString(), "Ok");
}
}
private showSnackBar(message: string) {
this.notificationService.push(message, "info");
private showSnackBar(message: string, action?: string) {
/*this.snackBar.open(message.toString(), action, {
duration: 3000,
});*/
console.log(message);
}
}

View File

@ -17,6 +17,7 @@ export class TableComponent {
@Input() columns: ColumnDefinition[] = [];
isBoolean(obje: any): boolean {
console.log(obje);
return typeof obje === "boolean";
}
}

View File

@ -1,54 +0,0 @@
{
"title": "Bienenkeeper",
"error": {
"Generic.SomethingWentWrong": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es später erneut."
},
"languages": {
"de": "Deutsch",
"en": "Englisch"
},
"menu": {
"settings": "Einstellungen",
"logout": "Ausloggen",
"theme": {
"dark": "Dunkel",
"light": "Hell"
}
},
"navigation": {
"home": "Startseite",
"colonies": "Völker"
},
"auth": {
"password": "Password",
"email": "E-Mail",
"username-or-email": "Benutzername oder E-Mail",
"username": "Benutzername",
"login": "Anmelden",
"registration": "Registrierung",
"register": "Registrieren",
"reset-password": "Passwort zurücksetzen",
"new-password": "Neues Passwort",
"password-confirmation": "Passwort wiederholen",
"reset-password-confirmation": "Neues Passwort setzen",
"already-registered": "Bereits registriert?",
"not-yet-registered": "Noch nicht registriert?",
"login-now": "Jetzt anmelden!",
"register-now": "Jetzt registrieren!",
"forgot-password": "Passwort vergessen",
"confirm-registration": "Registrierung bestätigen"
},
"settings": {
"tab-security": "Sicherheit",
"tab-profile": "Profil",
"security": {
"change-username": "Benutzername ändern",
"change-password": "Passwort ändern",
"current-password": "Aktuelles Passwort",
"new-password": "Neues Passwort",
"new-username": "Neuer Benutzername",
"password-confirmation": "Passwort wiederholen"
}
}
}

View File

@ -1,54 +0,0 @@
{
"title": "Beekeeper",
"error": {
"Generic.SomethingWentWrong": "An unexpected error has occurred. Please try again later."
},
"languages": {
"de": "German",
"en": "English"
},
"menu": {
"settings": "Settings",
"logout": "Logout",
"theme": {
"dark": "Dark",
"light": "Light"
}
},
"navigation": {
"home": "Home",
"colonies": "Colonies"
},
"auth": {
"password": "Password",
"email": "Email",
"username-or-email": "Username or Email",
"username": "Username",
"login": "Login",
"registration": "Registration",
"register": "Register",
"reset-password": "Reset Password",
"new-password": "New Password",
"password-confirmation": "Confirm Password",
"reset-password-confirmation": "Set New Password",
"already-registered": "Already registered?",
"not-yet-registered": "Not yet registered?",
"login-now": "Login now!",
"register-now": "Register now!",
"forgot-password": "Forgot Password",
"confirm-registration": "Confirm Registration"
},
"settings": {
"tab-security": "Security",
"tab-profile": "Profile",
"security": {
"change-username": "Change Username",
"change-password": "Change Password",
"current-password": "Current Password",
"new-password": "New Password",
"new-username": "New Username",
"password-confirmation": "Confirm Password"
}
}
}

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M17.4 9C17 7.8 16.2 7 15 6.5V5H14V6.4H13.6C12.5 6.4 11.6 6.8 10.8 7.6L10.4 8L9 7.5C8.7 7.4 8.4 7.3 8 7.3C7.4 7.3 6.8 7.5 6.3 7.9C5.7 8.3 5.4 8.8 5.2 9.3C5 10 5 10.6 5.2 11.3C5.5 12 5.8 12.5 6.3 12.8C5.9 14.3 6.2 15.6 7.3 16.7C8.1 17.5 9 17.9 10.1 17.9C10.6 17.9 10.9 17.9 11.2 17.8C11.8 18.6 12.6 19.1 13.6 19.1C13.9 19.1 14.3 19.1 14.6 19C15.2 18.8 15.6 18.4 16 17.9C16.4 17.3 16.6 16.8 16.6 16.2C16.6 15.8 16.6 15.5 16.5 15.2L16 13.6L16.6 13.2C17.4 12.4 17.8 11.3 17.7 10.1H19V9H17.4M7.7 11.3C7.1 11 6.9 10.6 7.1 10C7.3 9.4 7.7 9.2 8.3 9.4L11.5 10.6C9.9 11.4 8.7 11.6 7.7 11.3M14 16.9C13.4 17.1 13 16.9 12.7 16.3C12.4 15.3 12.6 14.1 13.4 12.5L14.6 15.6C14.8 16.3 14.6 16.7 14 16.9M15.2 11.6L14.6 10V9.9L14.3 9.6H14.2L12.6 9C13 8.7 13.4 8.5 13.9 8.5C14.4 8.5 14.9 8.7 15.3 9.1C15.7 9.5 15.9 9.9 15.9 10.4C15.7 10.7 15.5 11.2 15.2 11.6Z" /></svg>

Before

Width:  |  Height:  |  Size: 935 B

View File

@ -8,8 +8,8 @@
--color-text-primary-muted: 100, 100, 100;
--color-text-secondary: 255, 255, 255;
--color-text-secondary-muted: 170, 170, 170;
--color-text-accent: 250, 183, 0;
--color-text-accent-muted: 250, 183, 0;
--color-text-accent: 255, 199, 44;
--color-text-accent-muted: 255, 199, 44;
--color-primary: 244, 244, 245;
--color-secondary: 238, 238, 240;

View File

@ -12,7 +12,6 @@ module.exports = {
content: ["./src/**/*.{html,ts}", "./node_modules/flowbite/**/*.js"],
theme: {
extend: {
// "foregrounds"
textColor: {
skin: {
primary: withOpacity("--color-text-primary"),
@ -23,18 +22,6 @@ module.exports = {
"accent-muted": withOpacity("--color-text-accent-muted"),
},
},
fill: {
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"),
},
},
// "backgrounds"
backgroundColor: {
skin: {
primary: withOpacity("--color-primary"),
@ -42,8 +29,6 @@ module.exports = {
accent: withOpacity("--color-accent"),
},
},
// "delicate lines"
boxShadowColor: {
skin: {
primary: withOpacity("--color-text-secondary-muted"),