generated from flo/template-frontend
internationalization
This commit is contained in:
parent
5facddcb5a
commit
d8d09d7bac
@ -84,10 +84,10 @@
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "bee:build:production"
|
||||
"buildTarget": "bee:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "bee:build:development"
|
||||
"buildTarget": "bee:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
@ -95,7 +95,7 @@
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "bee:build"
|
||||
"buildTarget": "bee:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
|
||||
29
package.json
29
package.json
@ -9,24 +9,25 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@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",
|
||||
"@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",
|
||||
"flowbite": "^2.5.1",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.12.0"
|
||||
"zone.js": "~0.14.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^15.2.6",
|
||||
"@angular/cli": "~15.2.6",
|
||||
"@angular/compiler-cli": "^15.2.0",
|
||||
"@angular-devkit/build-angular": "^18.2.2",
|
||||
"@angular/cli": "~18.2.2",
|
||||
"@angular/compiler-cli": "^18.2.2",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
"jasmine-core": "~4.5.0",
|
||||
"karma": "~6.4.0",
|
||||
@ -35,6 +36,6 @@
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.0.0",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"typescript": "~4.9.4"
|
||||
"typescript": "~5.4.5"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
<router-outlet></router-outlet>
|
||||
<div class="absolute bottom-0 w-full">
|
||||
|
||||
<div class="absolute bottom-0 w-full z-50">
|
||||
<app-notification-bar />
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
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",
|
||||
@ -10,8 +11,13 @@ import { AppService } from "./core/services/app.service";
|
||||
export class AppComponent implements OnInit {
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private appService: AppService
|
||||
) {}
|
||||
private translate: TranslateService
|
||||
) {
|
||||
var language = navigator.language;
|
||||
|
||||
translate.setDefaultLang(language);
|
||||
translate.use(language);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.authService.readUserState();
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { BrowserModule } from "@angular/platform-browser";
|
||||
import { HttpClientModule } from "@angular/common/http";
|
||||
import {
|
||||
HttpClient,
|
||||
provideHttpClient,
|
||||
withInterceptorsFromDi,
|
||||
} 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";
|
||||
@ -25,18 +30,28 @@ 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: [],
|
||||
bootstrap: [AppComponent],
|
||||
providers: [provideHttpClient(withInterceptorsFromDi())],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@ -7,6 +7,7 @@ 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 },
|
||||
@ -38,6 +39,11 @@ const routes: Routes = [
|
||||
ForgotPasswordComponent,
|
||||
ResetPasswordComponent,
|
||||
],
|
||||
imports: [RouterModule.forChild(routes), CommonModule, ReactiveFormsModule],
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
TranslateModule,
|
||||
],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
@ -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">
|
||||
Beekeeper
|
||||
{{ "title" | translate }}
|
||||
</h1>
|
||||
<h1 class="font-bold text-center text-skin-accent text-xl">
|
||||
Registrierung Abschließen
|
||||
{{ "auth.confirm-registration" | translate }}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<label
|
||||
for="password"
|
||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
||||
>Passwort</label
|
||||
>{{ "auth.password" | translate }}</label
|
||||
>
|
||||
<input
|
||||
formControlName="password"
|
||||
@ -28,7 +28,7 @@
|
||||
<label
|
||||
for="passwordConfirmation"
|
||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
||||
>Passwort wiederholen</label
|
||||
>{{ "auth.password-confirmation" | translate }}</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"
|
||||
>
|
||||
Registrieren
|
||||
{{ "auth.confirm-registration" | translate }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@ -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">
|
||||
Beekeeper
|
||||
{{ "title" | translate }}
|
||||
</h1>
|
||||
<h1 class="font-bold text-center text-skin-accent text-xl">
|
||||
Passwort vergessen?
|
||||
{{ "auth.forgot-password" | translate }}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@ -14,7 +14,8 @@
|
||||
<label
|
||||
for="mail"
|
||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
||||
>E-Mail</label
|
||||
>
|
||||
{{ "auth.email" | translate }}</label
|
||||
>
|
||||
<input
|
||||
formControlName="mail"
|
||||
@ -30,6 +31,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"
|
||||
>
|
||||
Passwort zurücksetzen
|
||||
{{ "auth.reset-password" | translate }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@ -2,9 +2,11 @@
|
||||
|
||||
<div class="max-w-sm mx-auto mb-10">
|
||||
<h1 class="font-bold text-center text-skin-primary text-5xl mb-5">
|
||||
Beekeeper
|
||||
{{ "title" | translate }}
|
||||
</h1>
|
||||
<h1 class="font-bold text-center text-skin-accent text-xl">
|
||||
{{ "auth.login" | translate }}
|
||||
</h1>
|
||||
<h1 class="font-bold text-center text-skin-accent text-xl">Anmeldung</h1>
|
||||
</div>
|
||||
|
||||
<form class="max-w-sm mx-auto" [formGroup]="loginForm">
|
||||
@ -12,7 +14,7 @@
|
||||
<label
|
||||
for="identifier"
|
||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
||||
>Benutzername oder E-Mail</label
|
||||
>{{ "auth.username-or-email" | translate }}</label
|
||||
>
|
||||
<input
|
||||
formControlName="identifier"
|
||||
@ -26,11 +28,11 @@
|
||||
id="helper-text-explanation"
|
||||
class="mt-2 text-sm text-skin-primary-muted"
|
||||
>
|
||||
Neu hier?
|
||||
{{ "auth.not-yet-registered" | translate }}
|
||||
<a
|
||||
routerLink="/auth/registration"
|
||||
class="font-medium text-skin-accent hover:underline hover:font-bold"
|
||||
>Jetzt registrieren!</a
|
||||
>{{ "auth.register-now" | translate }}</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
@ -38,7 +40,7 @@
|
||||
<label
|
||||
for="password"
|
||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
||||
>Passwort</label
|
||||
>{{ "auth.password" | translate }}</label
|
||||
>
|
||||
<input
|
||||
formControlName="password"
|
||||
@ -54,7 +56,7 @@
|
||||
<a
|
||||
routerLink="/auth/forgot-password"
|
||||
class="font-medium text-skin-accent hover:underline hover:font-bold"
|
||||
>Passwort vergessen?</a
|
||||
>{{ "auth.forgot-password" | translate }}?</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
@ -64,6 +66,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"
|
||||
>
|
||||
Anmelden
|
||||
{{ "auth.login" | translate }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@ -2,9 +2,11 @@
|
||||
|
||||
<div class="max-w-sm mx-auto mb-10">
|
||||
<h1 class="font-bold text-center text-skin-primary text-5xl mb-5">
|
||||
Beekeeper
|
||||
{{ "title" | translate }}
|
||||
</h1>
|
||||
<h1 class="font-bold text-center text-skin-accent text-xl">
|
||||
{{ "auth.registration" | translate }}
|
||||
</h1>
|
||||
<h1 class="font-bold text-center text-skin-accent text-xl">Registrierung</h1>
|
||||
</div>
|
||||
|
||||
<form class="max-w-sm mx-auto" [formGroup]="registrationForm">
|
||||
@ -12,7 +14,8 @@
|
||||
<label
|
||||
for="mail"
|
||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
||||
>E-Mail</label
|
||||
>
|
||||
{{ "auth.email" | translate }}</label
|
||||
>
|
||||
<input
|
||||
formControlName="mail"
|
||||
@ -27,7 +30,8 @@
|
||||
<label
|
||||
for="username"
|
||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
||||
>Benutzername</label
|
||||
>
|
||||
{{ "auth.reset-password" | translate }}</label
|
||||
>
|
||||
<input
|
||||
formControlName="username"
|
||||
@ -40,11 +44,11 @@
|
||||
id="helper-text-explanation"
|
||||
class="mt-2 text-sm text-skin-primary-muted"
|
||||
>
|
||||
Bereits registiert?
|
||||
{{ "auth.already-registered" | translate }}
|
||||
<a
|
||||
routerLink="/auth/login"
|
||||
class="font-medium text-skin-accent hover:underline hover:font-bold"
|
||||
>Jetzt anmelden!</a
|
||||
>{{ "auth.login-now" | translate }}</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
@ -54,6 +58,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"
|
||||
>
|
||||
Registrieren
|
||||
{{ "auth.register" | translate }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@ -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">
|
||||
Beekeeper
|
||||
{{ "title" | translate }}
|
||||
</h1>
|
||||
<h1 class="font-bold text-center text-skin-accent text-xl">
|
||||
Passwort zurücksetzen
|
||||
{{ "auth.reset-password" | translate }}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<label
|
||||
for="newPassword"
|
||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
||||
>Neues Passwort</label
|
||||
>{{ "auth.new-password" | translate }}</label
|
||||
>
|
||||
<input
|
||||
formControlName="newPassword"
|
||||
@ -28,7 +28,7 @@
|
||||
<label
|
||||
for="passwordConfirmation"
|
||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
||||
>Passwort wiederholen</label
|
||||
>{{ "auth.password-confirmation" | translate }}</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"
|
||||
>
|
||||
Neues Passwort bestätigen
|
||||
{{ "auth.new-password" | translate }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
<app-navigation></app-navigation>
|
||||
|
||||
<button (click)="fehler()">Fehler</button>
|
||||
|
||||
<div class="max-w-screen-xl mx-auto p-4">
|
||||
<router-outlet></router-outlet>
|
||||
<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">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,18 +1,8 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { NotificationService } from "../../services/notification.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-home",
|
||||
templateUrl: "./home.component.html",
|
||||
styleUrls: ["./home.component.scss"],
|
||||
})
|
||||
export class HomeComponent {
|
||||
constructor(private notificationService: NotificationService) {}
|
||||
|
||||
fehler() {
|
||||
this.notificationService.push({
|
||||
message: "Ein Fehler ist aufgetreten :(",
|
||||
type: "danger",
|
||||
});
|
||||
}
|
||||
}
|
||||
export class HomeComponent {}
|
||||
|
||||
@ -1,139 +1,97 @@
|
||||
<nav class="bg-skin-accent">
|
||||
<div
|
||||
class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<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" alt="Beekeeper Logo" />
|
||||
<img src="assets/icon.png" class="h-10 w-10" alt="Beekeeper Logo" />
|
||||
<span
|
||||
class="text-skin-primary self-center text-4xl font-semibold whitespace-nowrap"
|
||||
>Bienenkeeper</span
|
||||
>{{ "title" | translate }}</span
|
||||
>
|
||||
</a>
|
||||
|
||||
<div class="flex items-center md:order-2 space-x-3 md:space-x-0">
|
||||
<!--User Bubble-->
|
||||
<button
|
||||
type="button"
|
||||
class="w-10 h-10 text-sm bg-skin-primary rounded-full hidden md:block 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>
|
||||
|
||||
<!--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"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<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-72 px-4">
|
||||
<div
|
||||
class="text-base list-none divide-y divide-skin-primary rounded-lg shadow-sm shadow-skin-primary bg-skin-primary"
|
||||
>
|
||||
<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
|
||||
class="w-full text-left block py-2 px-3 rounded text-skin-primary hover:bg-skin-accent"
|
||||
[routerLink]="
|
||||
usermenuButton.routerLink === undefined
|
||||
? null
|
||||
: [usermenuButton.routerLink]
|
||||
"
|
||||
(click)="clickUserMenuButtonLabel(usermenuButton)"
|
||||
>
|
||||
{{ getUserMenuButtonLabel(usermenuButton) }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation -->
|
||||
<div
|
||||
class="items-center justify-between hidden w-full md:flex md:w-auto md:order-1"
|
||||
id="navbar-user"
|
||||
<!--User Bubble-->
|
||||
<button
|
||||
type="button"
|
||||
class="w-12 h-12 text-sm bg-skin-primary rounded-full focus:ring-4 focus:ring-skin-primary"
|
||||
id="user-menu-button"
|
||||
aria-expanded="false"
|
||||
data-dropdown-toggle="user-dropdown"
|
||||
data-dropdown-placement="bottom"
|
||||
>
|
||||
<div
|
||||
class="text-base mt-4 list-none md:divide-y md:divide-skin-primary rounded-lg shadow-sm shadow-skin-primary md:hidden bg-skin-primary"
|
||||
>
|
||||
<div class="px-4 pt-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="flex flex-col text-xl p-4 rounded-lg bg-skin-primary md:bg-skin-accent md:space-x-8"
|
||||
>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<span class="text-skin-accent rounded-full font-bold p-2 uppercase">{{
|
||||
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"
|
||||
>
|
||||
<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
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- User dropdown -->
|
||||
<div id="user-dropdown" class="hidden min-w-full md:min-w-64 px-4 z-50">
|
||||
<div
|
||||
class="text-base list-none divide-y divide-skin-primary rounded-lg shadow-sm shadow-skin-primary bg-skin-primary"
|
||||
>
|
||||
<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
|
||||
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]
|
||||
"
|
||||
(click)="clickUserMenuButtonLabel(usermenuButton)"
|
||||
>
|
||||
{{ getUserMenuButtonLabel(usermenuButton) | translate }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -3,11 +3,13 @@ import { initFlowbite } from "flowbite";
|
||||
import { AuthService } from "../../services/auth.service";
|
||||
import { UserStateResponse } from "../../models/user-state-request.model";
|
||||
import { Router } from "@angular/router";
|
||||
import { filter, map } from "rxjs";
|
||||
import { filter } from "rxjs";
|
||||
import { AppService } from "../../services/app.service";
|
||||
import { NotificationService } from "../../services/notification.service";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
|
||||
interface NavigationLink {
|
||||
label: string;
|
||||
label: Function | string;
|
||||
routerLink: string;
|
||||
}
|
||||
|
||||
@ -24,25 +26,45 @@ interface UserMenuButton {
|
||||
})
|
||||
export class NavigationComponent implements OnInit {
|
||||
navigationLinks: NavigationLink[] = [
|
||||
{ routerLink: "/", label: "Startseite" },
|
||||
{ routerLink: "/settings", label: "Völker" },
|
||||
{ routerLink: "/", label: () => "navigation.home" },
|
||||
{
|
||||
routerLink: "/colonies",
|
||||
label: () => "navigation.colonies",
|
||||
},
|
||||
];
|
||||
|
||||
usermenuButtons: UserMenuButton[] = [
|
||||
{
|
||||
label: "Einstellungen",
|
||||
label: () => "menu.settings",
|
||||
routerLink: "/settings",
|
||||
clickCallback: undefined,
|
||||
},
|
||||
{
|
||||
label: () => (this.darkMode ? "Hell" : "Dunkel"),
|
||||
label: () => (this.darkMode ? "menu.theme.light" : "menu.theme.dark"),
|
||||
routerLink: undefined,
|
||||
clickCallback: () => {
|
||||
this.toggleDarkmode();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Ausloggen",
|
||||
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",
|
||||
routerLink: undefined,
|
||||
clickCallback: () => {
|
||||
this.logout();
|
||||
@ -52,13 +74,28 @@ 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))
|
||||
@ -80,6 +117,19 @@ 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();
|
||||
@ -93,4 +143,11 @@ export class NavigationComponent implements OnInit {
|
||||
return button.label;
|
||||
}
|
||||
}
|
||||
getNavigationLinkLabel(link: NavigationLink) {
|
||||
if (typeof link.label === "function") {
|
||||
return link.label();
|
||||
} else {
|
||||
return link.label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<div
|
||||
*ngFor="let element of elements"
|
||||
*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
|
||||
@ -13,14 +13,14 @@
|
||||
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">{{ element.type }}</span>
|
||||
<span class="sr-only">{{ item.element.type }}</span>
|
||||
<div class="ms-3 text-sm font-medium">
|
||||
{{ element.message }}
|
||||
{{ 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(element)"
|
||||
(click)="dismiss(item)"
|
||||
>
|
||||
<span class="sr-only">Close</span>
|
||||
<svg
|
||||
|
||||
@ -1,10 +1,15 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { NotificationService } from "../../services/notification.service";
|
||||
import { filter } from "rxjs";
|
||||
|
||||
export interface NotificationElement {
|
||||
message: string;
|
||||
type: "info" | "danger" | "warn";
|
||||
type: "info" | "danger" | "warn" | "success";
|
||||
timeout: number | undefined;
|
||||
}
|
||||
|
||||
interface NotificationItem {
|
||||
element: NotificationElement;
|
||||
shownAt: Date;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -13,17 +18,52 @@ export interface NotificationElement {
|
||||
styleUrls: ["./notification-bar.component.scss"],
|
||||
})
|
||||
export class NotificationBarComponent {
|
||||
elements: NotificationElement[] = [];
|
||||
items: NotificationItem[] = [];
|
||||
isRunning: boolean;
|
||||
|
||||
constructor(private notificationService: NotificationService) {
|
||||
this.isRunning = false;
|
||||
|
||||
this.notificationService.notification$.subscribe((notification) => {
|
||||
if (notification !== undefined) {
|
||||
this.elements.push(notification);
|
||||
this.items.push({
|
||||
element: notification,
|
||||
shownAt: new Date(),
|
||||
});
|
||||
|
||||
this.startTimeoutIfNecessary();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dismiss(element: NotificationElement): void {
|
||||
this.elements = this.elements.filter((e) => e !== element);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
aria-controls="profile"
|
||||
aria-selected="false"
|
||||
>
|
||||
Profil
|
||||
{{ "settings.tab-profile" | translate }}
|
||||
</button>
|
||||
</li>
|
||||
<li class="me-2" role="presentation">
|
||||
@ -40,7 +40,7 @@
|
||||
aria-controls="security"
|
||||
aria-selected="false"
|
||||
>
|
||||
Sicherheit
|
||||
{{ "settings.tab-security" | translate }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
interface User {
|
||||
username: string;
|
||||
mail: string;
|
||||
age: number;
|
||||
}
|
||||
|
||||
@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 {}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
<div class="grid grid-cols-1 md:grid-cols-2">
|
||||
<div>
|
||||
<h2>Passwort ändern</h2>
|
||||
<h2>{{ "settings.security.change-password" | translate }}</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"
|
||||
>Aktuelles Passwort</label
|
||||
>{{ "settings.security.current-password" | translate }}</label
|
||||
>
|
||||
<input
|
||||
formControlName="password"
|
||||
@ -21,7 +21,7 @@
|
||||
<label
|
||||
for="newPassword"
|
||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
||||
>Neues Passwort</label
|
||||
>{{ "settings.security.new-password" | translate }}</label
|
||||
>
|
||||
<input
|
||||
formControlName="newPassword"
|
||||
@ -36,7 +36,7 @@
|
||||
<label
|
||||
for="newPasswordConfirmation"
|
||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
||||
>Neues Passwort bestätigen</label
|
||||
>{{ "settings.security.password-confirmation" | translate }}</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"
|
||||
>
|
||||
Passwort ändern
|
||||
{{ "settings.security.change-password" | translate }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>Benutername ändern</h2>
|
||||
<h2>{{ "settings.security.change-username" | translate }}</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"
|
||||
>Aktuelles Passwort</label
|
||||
>{{ "settings.security.current-password" | translate }}</label
|
||||
>
|
||||
<input
|
||||
formControlName="password"
|
||||
@ -80,7 +80,7 @@
|
||||
<label
|
||||
for="newUsername"
|
||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
||||
>Neuer Benutzername</label
|
||||
>{{ "settings.security.new-username" | translate }}</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"
|
||||
>
|
||||
Benutzername ändern
|
||||
{{ "settings.security.change-username" | translate }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -13,6 +13,7 @@ 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: [
|
||||
@ -29,7 +30,13 @@ import { NotificationService } from "./services/notification.service";
|
||||
TabSecurityComponent,
|
||||
NotificationBarComponent,
|
||||
],
|
||||
imports: [CommonModule, SharedModule, RouterModule, ReactiveFormsModule],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
RouterModule,
|
||||
ReactiveFormsModule,
|
||||
TranslateModule,
|
||||
],
|
||||
providers: [
|
||||
AuthGuard,
|
||||
AuthService,
|
||||
|
||||
@ -1,16 +1,10 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import {
|
||||
ActivatedRouteSnapshot,
|
||||
CanActivate,
|
||||
Router,
|
||||
RouterStateSnapshot,
|
||||
UrlTree,
|
||||
} from "@angular/router";
|
||||
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot, UrlTree } from "@angular/router";
|
||||
import { filter, map, Observable } from "rxjs";
|
||||
import { AuthService } from "../services/auth.service";
|
||||
|
||||
@Injectable()
|
||||
export class AuthGuard implements CanActivate {
|
||||
export class AuthGuard {
|
||||
constructor(private authService: AuthService, private router: Router) {}
|
||||
|
||||
canActivate(): Observable<boolean> | boolean {
|
||||
|
||||
@ -42,7 +42,6 @@ export class AuthService {
|
||||
"user/state",
|
||||
{},
|
||||
(response: UserStateResponse) => {
|
||||
console.log("set next state");
|
||||
this.currentState$.next(response);
|
||||
},
|
||||
() => {
|
||||
|
||||
@ -6,8 +6,29 @@ import { NotificationElement } from "../components/notification-bar/notification
|
||||
export class NotificationService {
|
||||
notification$ = new Subject<NotificationElement>();
|
||||
|
||||
push(notification: NotificationElement): void {
|
||||
console.log(notification.type + ": " + notification.message);
|
||||
this.notification$.next(notification);
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ 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",
|
||||
@ -10,7 +11,8 @@ export class RequestService {
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private router: Router,
|
||||
private notificationService: NotificationService
|
||||
private notificationService: NotificationService,
|
||||
private translate: TranslateService
|
||||
) {}
|
||||
|
||||
public post(
|
||||
@ -85,7 +87,7 @@ export class RequestService {
|
||||
|
||||
private handleError(answer: any): void {
|
||||
if (answer.status == 401) {
|
||||
console.log("Deine Sitzung konnte nicht gefunden werden");
|
||||
this.showSnackBar("Deine Sitzung konnte nicht gefunden werden");
|
||||
this.router.navigate(["/auth"]);
|
||||
return;
|
||||
}
|
||||
@ -103,7 +105,10 @@ export class RequestService {
|
||||
}
|
||||
|
||||
if (errorObject.hasOwnProperty("code")) {
|
||||
throw errorObject.code.toString();
|
||||
throw (
|
||||
this.translate.instant("error." + errorObject.code.toString()) ??
|
||||
errorObject.code.toString()
|
||||
);
|
||||
}
|
||||
} catch (error: any) {
|
||||
this.showSnackBar(error.toString());
|
||||
@ -111,9 +116,6 @@ export class RequestService {
|
||||
}
|
||||
|
||||
private showSnackBar(message: string) {
|
||||
this.notificationService.push({
|
||||
type: "info",
|
||||
message: message,
|
||||
});
|
||||
this.notificationService.push(message, "info");
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@ export class TableComponent {
|
||||
@Input() columns: ColumnDefinition[] = [];
|
||||
|
||||
isBoolean(obje: any): boolean {
|
||||
console.log(obje);
|
||||
return typeof obje === "boolean";
|
||||
}
|
||||
}
|
||||
|
||||
54
src/assets/i18n/de.json
Normal file
54
src/assets/i18n/de.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/assets/i18n/en.json
Normal file
54
src/assets/i18n/en.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
1
src/assets/icon.svg
Normal file
1
src/assets/icon.svg
Normal file
@ -0,0 +1 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 935 B |
@ -12,6 +12,7 @@ module.exports = {
|
||||
content: ["./src/**/*.{html,ts}", "./node_modules/flowbite/**/*.js"],
|
||||
theme: {
|
||||
extend: {
|
||||
// "foregrounds"
|
||||
textColor: {
|
||||
skin: {
|
||||
primary: withOpacity("--color-text-primary"),
|
||||
@ -22,6 +23,18 @@ 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"),
|
||||
@ -29,6 +42,8 @@ module.exports = {
|
||||
accent: withOpacity("--color-accent"),
|
||||
},
|
||||
},
|
||||
|
||||
// "delicate lines"
|
||||
boxShadowColor: {
|
||||
skin: {
|
||||
primary: withOpacity("--color-text-secondary-muted"),
|
||||
|
||||
Reference in New Issue
Block a user