generated from flo/template-frontend
changes
This commit is contained in:
parent
d8d09d7bac
commit
1f99cfb53c
@ -15,8 +15,12 @@ export class AppComponent implements OnInit {
|
|||||||
) {
|
) {
|
||||||
var language = navigator.language;
|
var language = navigator.language;
|
||||||
|
|
||||||
translate.setDefaultLang(language);
|
this.translate.setDefaultLang(language);
|
||||||
translate.use(language);
|
this.translate.use(language);
|
||||||
|
|
||||||
|
this.translate.onLangChange.subscribe(() => {
|
||||||
|
document.title = this.translate.instant("title");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { ReactiveFormsModule } from "@angular/forms";
|
|||||||
import { ForgotPasswordComponent } from "./components/forgot-password/forgot-password.component";
|
import { ForgotPasswordComponent } from "./components/forgot-password/forgot-password.component";
|
||||||
import { ResetPasswordComponent } from "./components/reset-password/reset-password.component";
|
import { ResetPasswordComponent } from "./components/reset-password/reset-password.component";
|
||||||
import { TranslateModule } from "@ngx-translate/core";
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
|
import { SharedModule } from "src/app/shared/shared.module";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: "login", component: LoginComponent },
|
{ path: "login", component: LoginComponent },
|
||||||
@ -44,6 +45,7 @@ const routes: Routes = [
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
|
SharedModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AuthModule {}
|
export class AuthModule {}
|
||||||
|
|||||||
@ -11,39 +11,20 @@
|
|||||||
|
|
||||||
<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
|
<shared-input
|
||||||
for="password"
|
key="password"
|
||||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
inputType="password"
|
||||||
>{{ "auth.password" | translate }}</label
|
label="auth.password"
|
||||||
>
|
required="true"
|
||||||
<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
|
<shared-input
|
||||||
for="passwordConfirmation"
|
key="passwordConfirmation"
|
||||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
inputType="password"
|
||||||
>{{ "auth.password-confirmation" | translate }}</label
|
label="auth.password-confirmation"
|
||||||
>
|
required="true"
|
||||||
<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
|
<shared-submit label="auth.confirm-registration" (onSubmit)="confirm()" />
|
||||||
(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"
|
|
||||||
>
|
|
||||||
{{ "auth.confirm-registration" | translate }}
|
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -40,5 +40,7 @@ export class ConfirmRegistrationComponent {
|
|||||||
passwordConfirmation:
|
passwordConfirmation:
|
||||||
this.confirmRegistrationForm.value.passwordConfirmation!,
|
this.confirmRegistrationForm.value.passwordConfirmation!,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.router.navigateByUrl("/auth/login");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,26 +11,12 @@
|
|||||||
|
|
||||||
<form class="max-w-sm mx-auto" [formGroup]="forgotPasswordForm">
|
<form class="max-w-sm mx-auto" [formGroup]="forgotPasswordForm">
|
||||||
<div class="mb-5">
|
<div class="mb-5">
|
||||||
<label
|
<shared-input
|
||||||
for="mail"
|
key="mail"
|
||||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
inputType="email"
|
||||||
>
|
label="auth.email"
|
||||||
{{ "auth.email" | translate }}</label
|
required="true"
|
||||||
>
|
|
||||||
<input
|
|
||||||
formControlName="mail"
|
|
||||||
type="email"
|
|
||||||
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"
|
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<shared-submit label="auth.reset-password" (onSubmit)="confirm()" />
|
||||||
(click)="confirm()"
|
|
||||||
[disabled]="!forgotPasswordForm.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"
|
|
||||||
>
|
|
||||||
{{ "auth.reset-password" | translate }}
|
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -11,23 +11,13 @@
|
|||||||
|
|
||||||
<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
|
<shared-input
|
||||||
for="identifier"
|
key="identifier"
|
||||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
inputType="text"
|
||||||
>{{ "auth.username-or-email" | translate }}</label
|
label="auth.username-or-email"
|
||||||
>
|
required="true"
|
||||||
<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
|
|
||||||
/>
|
/>
|
||||||
<p
|
<p class="mt-2 text-sm text-skin-primary-muted">
|
||||||
id="helper-text-explanation"
|
|
||||||
class="mt-2 text-sm text-skin-primary-muted"
|
|
||||||
>
|
|
||||||
{{ "auth.not-yet-registered" | translate }}
|
{{ "auth.not-yet-registered" | translate }}
|
||||||
<a
|
<a
|
||||||
routerLink="/auth/registration"
|
routerLink="/auth/registration"
|
||||||
@ -37,22 +27,13 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-5">
|
<div class="mb-5">
|
||||||
<label
|
<shared-input
|
||||||
for="password"
|
key="password"
|
||||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
inputType="password"
|
||||||
>{{ "auth.password" | translate }}</label
|
label="auth.password"
|
||||||
>
|
required="true"
|
||||||
<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
|
<p class="mt-2 text-sm text-skin-primary-muted">
|
||||||
id="helper-text-explanation"
|
|
||||||
class="mt-2 text-sm text-skin-primary-muted"
|
|
||||||
>
|
|
||||||
<a
|
<a
|
||||||
routerLink="/auth/forgot-password"
|
routerLink="/auth/forgot-password"
|
||||||
class="font-medium text-skin-accent hover:underline hover:font-bold"
|
class="font-medium text-skin-accent hover:underline hover:font-bold"
|
||||||
@ -60,12 +41,6 @@
|
|||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
(click)="login()"
|
<shared-submit label="auth.login" (onSubmit)="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"
|
|
||||||
>
|
|
||||||
{{ "auth.login" | translate }}
|
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -11,34 +11,12 @@
|
|||||||
|
|
||||||
<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
|
<shared-input
|
||||||
for="mail"
|
key="mail"
|
||||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
inputType="email"
|
||||||
>
|
label="auth.email"
|
||||||
{{ "auth.email" | translate }}</label
|
required="true"
|
||||||
>
|
|
||||||
<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"
|
placeholder="your@email.com"
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="mb-5">
|
|
||||||
<label
|
|
||||||
for="username"
|
|
||||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
|
||||||
>
|
|
||||||
{{ "auth.reset-password" | translate }}</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
|
<p
|
||||||
id="helper-text-explanation"
|
id="helper-text-explanation"
|
||||||
@ -52,12 +30,13 @@
|
|||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<div class="mb-5">
|
||||||
(click)="register()"
|
<shared-input
|
||||||
[disabled]="!registrationForm.valid"
|
key="username"
|
||||||
type="submit"
|
inputType="string"
|
||||||
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"
|
label="auth.username"
|
||||||
>
|
required="true"
|
||||||
{{ "auth.register" | translate }}
|
/>
|
||||||
</button>
|
</div>
|
||||||
|
<shared-submit label="auth.register" (onSubmit)="register()" />
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -11,39 +11,20 @@
|
|||||||
|
|
||||||
<form class="max-w-sm mx-auto" [formGroup]="resetPasswordForm">
|
<form class="max-w-sm mx-auto" [formGroup]="resetPasswordForm">
|
||||||
<div class="mb-5">
|
<div class="mb-5">
|
||||||
<label
|
<shared-input
|
||||||
for="newPassword"
|
key="newPassword"
|
||||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
inputType="password"
|
||||||
>{{ "auth.new-password" | translate }}</label
|
label="auth.new-password"
|
||||||
>
|
required="true"
|
||||||
<input
|
|
||||||
formControlName="newPassword"
|
|
||||||
type="password"
|
|
||||||
id="newPassword"
|
|
||||||
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
|
<shared-input
|
||||||
for="passwordConfirmation"
|
key="passwordConfirmation"
|
||||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
inputType="password"
|
||||||
>{{ "auth.password-confirmation" | translate }}</label
|
label="auth.password-confirmation"
|
||||||
>
|
required="true"
|
||||||
<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
|
<shared-submit label="auth.reset-password" (onSubmit)="confirm()" />
|
||||||
(click)="confirm()"
|
|
||||||
[disabled]="!resetPasswordForm.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"
|
|
||||||
>
|
|
||||||
{{ "auth.new-password" | translate }}
|
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -21,18 +21,27 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<a routerLink="/" class="flex items-center space-x-3">
|
<a routerLink="/" class="text-skin-primary flex items-center space-x-3"
|
||||||
<img src="assets/icon.png" class="h-10 w-10" alt="Beekeeper Logo" />
|
><svg
|
||||||
<span
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="text-skin-primary self-center text-4xl font-semibold whitespace-nowrap"
|
viewBox="4 4 16 16"
|
||||||
>{{ "title" | translate }}</span
|
class="w-12 h-12"
|
||||||
>
|
>
|
||||||
|
<title>{{ "title" | translate }}</title>
|
||||||
|
<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>
|
||||||
|
<span class="self-center text-4xl font-semibold whitespace-nowrap">{{
|
||||||
|
"title" | translate
|
||||||
|
}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!--User Bubble-->
|
<!--User Bubble-->
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="w-12 h-12 text-sm bg-skin-primary rounded-full focus:ring-4 focus:ring-skin-primary"
|
class="w-12 h-12 text-sm bg-skin-primary rounded-full focus:ring-4 focus:ring-skin-accent"
|
||||||
id="user-menu-button"
|
id="user-menu-button"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
data-dropdown-toggle="user-dropdown"
|
data-dropdown-toggle="user-dropdown"
|
||||||
@ -57,13 +66,26 @@
|
|||||||
class="block py-2 px-3 rounded text-skin-primary hover:text-skin-secondary hover:bg-skin-accent"
|
class="block py-2 px-3 rounded text-skin-primary hover:text-skin-secondary hover:bg-skin-accent"
|
||||||
[routerLink]="navigationLink.routerLink"
|
[routerLink]="navigationLink.routerLink"
|
||||||
[routerLinkActiveOptions]="{ exact: true }"
|
[routerLinkActiveOptions]="{ exact: true }"
|
||||||
routerLinkActive="font-bold"
|
routerLinkActive="underline"
|
||||||
>{{ getNavigationLinkLabel(navigationLink) | translate }}</a
|
|
||||||
>
|
>
|
||||||
|
<div class="flex flex-row gap-4">
|
||||||
|
<svg
|
||||||
|
*ngIf="navigationLink.path !== undefined"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
class="w-8 h-8 basis-auto"
|
||||||
|
>
|
||||||
|
<title>{{ navigationLink.label | translate }}</title>
|
||||||
|
<path fill="currentColor" [attr.d]="navigationLink.path" />
|
||||||
|
</svg>
|
||||||
|
<div class="basis-full">
|
||||||
|
{{ navigationLink.label | translate }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- User dropdown -->
|
<!-- 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-64 px-4 z-50">
|
||||||
<div
|
<div
|
||||||
@ -81,7 +103,7 @@
|
|||||||
<ul class="p-4" aria-labelledby="user-menu-button">
|
<ul class="p-4" aria-labelledby="user-menu-button">
|
||||||
<li *ngFor="let usermenuButton of usermenuButtons">
|
<li *ngFor="let usermenuButton of usermenuButtons">
|
||||||
<button
|
<button
|
||||||
class="w-full text-center block py-2 px-3 rounded text-skin-primary hover:bg-skin-accent"
|
class="block py-2 px-3 rounded text-skin-primary hover:text-skin-secondary hover:bg-skin-accent w-full text-right"
|
||||||
[routerLink]="
|
[routerLink]="
|
||||||
usermenuButton.routerLink === undefined
|
usermenuButton.routerLink === undefined
|
||||||
? null
|
? null
|
||||||
|
|||||||
@ -9,7 +9,8 @@ import { NotificationService } from "../../services/notification.service";
|
|||||||
import { TranslateService } from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
|
|
||||||
interface NavigationLink {
|
interface NavigationLink {
|
||||||
label: Function | string;
|
path: string | undefined;
|
||||||
|
label: string;
|
||||||
routerLink: string;
|
routerLink: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,10 +27,20 @@ interface UserMenuButton {
|
|||||||
})
|
})
|
||||||
export class NavigationComponent implements OnInit {
|
export class NavigationComponent implements OnInit {
|
||||||
navigationLinks: NavigationLink[] = [
|
navigationLinks: NavigationLink[] = [
|
||||||
{ routerLink: "/", label: () => "navigation.home" },
|
{
|
||||||
|
routerLink: "/",
|
||||||
|
path: "M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z",
|
||||||
|
label: "navigation.home",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
routerLink: "/settings",
|
||||||
|
path: "M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z",
|
||||||
|
label: "menu.settings",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
routerLink: "/colonies",
|
routerLink: "/colonies",
|
||||||
label: () => "navigation.colonies",
|
path: "M23 14.5C23 12.94 21.97 11.63 20.55 11.18C20.83 10.68 21 10.11 21 9.5C21 7.94 19.97 6.63 18.55 6.18C18.83 5.68 19 5.11 19 4.5C19 2.57 17.43 1 15.5 1H8.5C6.57 1 5 2.57 5 4.5C5 5.11 5.17 5.68 5.45 6.18C4.04 6.63 3 7.94 3 9.5C3 10.11 3.17 10.68 3.45 11.18C2.04 11.63 1 12.94 1 14.5C1 15.76 1.67 16.84 2.67 17.46C2.25 18.03 2 18.74 2 19.5C2 21.43 3.57 23 5.5 23H18.5C20.43 23 22 21.43 22 19.5C22 18.74 21.75 18.03 21.33 17.46C22.33 16.84 23 15.76 23 14.5M8.5 3H15.5C16.33 3 17 3.67 17 4.5S16.33 6 15.5 6H8.5C7.67 6 7 5.33 7 4.5S7.67 3 8.5 3M6.5 8H17.5C18.33 8 19 8.67 19 9.5S18.33 11 17.5 11H6.5C5.67 11 5 10.33 5 9.5S5.67 8 6.5 8M4 19.5C4 18.67 4.67 18 5.5 18H9V21H5.5C4.67 21 4 20.33 4 19.5M18.5 21H15V18H18.5C19.33 18 20 18.67 20 19.5S19.33 21 18.5 21M19.5 16H14.82C14.4 14.84 13.3 14 12 14S9.6 14.84 9.18 16H4.5C3.67 16 3 15.33 3 14.5S3.67 13 4.5 13H19.5C20.33 13 21 13.67 21 14.5S20.33 16 19.5 16Z",
|
||||||
|
label: "navigation.colonies",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -143,11 +154,4 @@ export class NavigationComponent implements OnInit {
|
|||||||
return button.label;
|
return button.label;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getNavigationLinkLabel(link: NavigationLink) {
|
|
||||||
if (typeof link.label === "function") {
|
|
||||||
return link.label();
|
|
||||||
} else {
|
|
||||||
return link.label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="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"
|
class="ms-auto -mx-1.5 -my-1.5 bg-skin-secondary text-skin-accent rounded-lg focus:ring-2 focus:ring-skin-accent p-1.5 hover:bg-skin-primary inline-flex items-center justify-center h-8 w-8"
|
||||||
(click)="dismiss(item)"
|
(click)="dismiss(item)"
|
||||||
>
|
>
|
||||||
<span class="sr-only">Close</span>
|
<span class="sr-only">Close</span>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<div class="rounded-lg shadow-sm shadow-skin-primary bg-skin-secondary p-5">
|
<div class="rounded-lg shadow-sm shadow-skin-primary bg-skin-secondary p-5">
|
||||||
<div class="inline-block text-skin-secondary bg-skin-accent rounded-lg p-2">
|
<div class="inline-block text-skin-primary bg-skin-accent rounded-lg p-2">
|
||||||
<div class="font-bold">
|
<div class="font-bold">
|
||||||
{{ state.username }}
|
{{ state.username }}
|
||||||
</div>
|
</div>
|
||||||
@ -10,57 +10,49 @@
|
|||||||
|
|
||||||
<div class="mb-4 border-b border-skin-primary">
|
<div class="mb-4 border-b border-skin-primary">
|
||||||
<ul
|
<ul
|
||||||
class="flex flex-wrap -mb-px text-sm font-medium text-center"
|
role="tablist"
|
||||||
id="default-styled-tab"
|
class="flex flex-nowrap -mb-px text-sm font-medium text-center"
|
||||||
data-tabs-toggle="#default-styled-tab-content"
|
data-tabs-toggle="#tab-outlet"
|
||||||
data-tabs-active-classes="text-skin-accent border-skin-accent"
|
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"
|
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 w-full xl:w-auto" role="presentation">
|
||||||
<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"
|
role="tab"
|
||||||
aria-controls="profile"
|
class="block p-4 w-full xl:w-auto border-b-2 rounded-t-lg"
|
||||||
aria-selected="false"
|
data-tabs-target="#profile"
|
||||||
>
|
>
|
||||||
{{ "settings.tab-profile" | translate }}
|
{{ "settings.tab-profile" | translate }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="me-2" role="presentation">
|
<li class="me-2 last:me-0 w-full xl:w-auto" role="presentation">
|
||||||
<button
|
<button
|
||||||
class="inline-block p-4 border-b-2 rounded-t-lg"
|
class="block p-4 w-full xl:w-auto border-b-2 rounded-t-lg"
|
||||||
id="security-styled-tab"
|
data-tabs-target="#security"
|
||||||
data-tabs-target="#styled-security"
|
|
||||||
type="button"
|
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-controls="security"
|
|
||||||
aria-selected="false"
|
|
||||||
>
|
>
|
||||||
{{ "settings.tab-security" | translate }}
|
{{ "settings.tab-security" | translate }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="default-styled-tab-content" class="text-skin-primary">
|
<div id="tab-outlet" class="text-skin-primary">
|
||||||
<div
|
<div role="tabpanel" class="hidden py-4 xl:p-4 rounded-lg" id="profile">
|
||||||
class="hidden p-4 rounded-lg"
|
|
||||||
id="styled-profile"
|
|
||||||
role="tabpanel"
|
|
||||||
aria-labelledby="profile-tab"
|
|
||||||
>
|
|
||||||
<app-tab-profile />
|
<app-tab-profile />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div role="tabpanel" class="hidden py-4 xl:p-4 rounded-lg" id="security">
|
||||||
class="hidden p-4 rounded-lg"
|
|
||||||
id="styled-security"
|
|
||||||
role="tabpanel"
|
|
||||||
aria-labelledby="security-tab"
|
|
||||||
>
|
|
||||||
<app-tab-security />
|
<app-tab-security />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<shared-tab-control [tabs]="tabs">
|
||||||
|
<div role="tabpanel" class="hidden py-4 xl:p-4 rounded-lg" id="profile">
|
||||||
|
<app-tab-profile />
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="hidden py-4 xl:p-4 rounded-lg" id="security">
|
||||||
|
<app-tab-security />
|
||||||
|
</div>
|
||||||
|
</shared-tab-control>
|
||||||
|
-->
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
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 { filter } from "rxjs";
|
|
||||||
import { UserStateResponse } from "../../models/user-state-request.model";
|
import { UserStateResponse } from "../../models/user-state-request.model";
|
||||||
|
import { TabItem } from "src/app/shared/models/tab-item.model";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-settings",
|
selector: "app-settings",
|
||||||
@ -11,6 +11,16 @@ import { UserStateResponse } from "../../models/user-state-request.model";
|
|||||||
})
|
})
|
||||||
export class SettingsComponent implements OnInit {
|
export class SettingsComponent implements OnInit {
|
||||||
state: UserStateResponse;
|
state: UserStateResponse;
|
||||||
|
tabs: TabItem[] = [
|
||||||
|
{
|
||||||
|
label: "settings.tab-profile",
|
||||||
|
target: "profile",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "settings.tab-security",
|
||||||
|
target: "security",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
constructor(private authService: AuthService) {
|
constructor(private authService: AuthService) {
|
||||||
this.state = this.authService.currentState$.value!;
|
this.state = this.authService.currentState$.value!;
|
||||||
|
|||||||
@ -0,0 +1,2 @@
|
|||||||
|
<shared-table [items]="items" [columns]="columns" [rowActions]="rowActions">
|
||||||
|
</shared-table>
|
||||||
@ -1,9 +1,15 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
import { NotificationService } from "src/app/core/services/notification.service";
|
||||||
|
import {
|
||||||
|
ColumnDefinition,
|
||||||
|
RowAction,
|
||||||
|
} from "src/app/shared/components/table/table.component";
|
||||||
|
|
||||||
interface User {
|
interface Product {
|
||||||
username: string;
|
description: string;
|
||||||
mail: string;
|
id: string;
|
||||||
age: number;
|
price: number;
|
||||||
|
stock: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -11,4 +17,75 @@ interface User {
|
|||||||
templateUrl: "./tab-profile.component.html",
|
templateUrl: "./tab-profile.component.html",
|
||||||
styleUrls: ["./tab-profile.component.scss"],
|
styleUrls: ["./tab-profile.component.scss"],
|
||||||
})
|
})
|
||||||
export class TabProfileComponent {}
|
export class TabProfileComponent {
|
||||||
|
constructor(private notificationService: NotificationService) {}
|
||||||
|
|
||||||
|
columns: ColumnDefinition[] = [
|
||||||
|
{
|
||||||
|
label: "test.product",
|
||||||
|
content: (item: Product) => item.description,
|
||||||
|
routerLink: (item: Product) => ["/", "product", item.id],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "test.price",
|
||||||
|
content: (item: Product) => item.price,
|
||||||
|
routerLink: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "test.delete",
|
||||||
|
content: "Löschen",
|
||||||
|
routerLink: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "test.stock",
|
||||||
|
content: (item: Product) => item.stock,
|
||||||
|
routerLink: undefined,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
rowActions: RowAction[] = [
|
||||||
|
{
|
||||||
|
label: "test.edit",
|
||||||
|
path: "M20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L15.12,5.12L18.87,8.87M3,17.25V21H6.75L17.81,9.93L14.06,6.18L3,17.25Z",
|
||||||
|
action: (item: Product) => {
|
||||||
|
this.edit(item);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "test.delete",
|
||||||
|
path: "M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z",
|
||||||
|
action: (item: Product) => {
|
||||||
|
this.delete(item);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
items: Product[] = [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
description: "Flüssiger Honig",
|
||||||
|
price: 6.5,
|
||||||
|
stock: 40,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
description: "Cremiger Honig",
|
||||||
|
price: 5,
|
||||||
|
stock: 32,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3",
|
||||||
|
description: "Labello",
|
||||||
|
price: 2.3,
|
||||||
|
stock: 7,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
delete(item: Product) {
|
||||||
|
this.notificationService.push(item.description + " gelöscht", "info");
|
||||||
|
}
|
||||||
|
|
||||||
|
edit(item: Product) {
|
||||||
|
this.notificationService.push(item.description + " bearbeitet", "info");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,104 +1,66 @@
|
|||||||
<div class="grid grid-cols-1 md:grid-cols-2">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-10">
|
||||||
<div>
|
<div>
|
||||||
<h2>{{ "settings.security.change-password" | translate }}</h2>
|
<h2>{{ "settings.security.change-password" | translate }}</h2>
|
||||||
<form class="mx-auto p-5" [formGroup]="changePasswordForm">
|
<form class="mx-auto py-5" [formGroup]="changePasswordForm">
|
||||||
<div class="mb-5">
|
<div class="mb-5">
|
||||||
<label
|
<shared-input
|
||||||
for="password"
|
key="password"
|
||||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
inputType="password"
|
||||||
>{{ "settings.security.current-password" | translate }}</label
|
label="settings.security.current-password"
|
||||||
>
|
required="true"
|
||||||
<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
|
<shared-input
|
||||||
for="newPassword"
|
key="newPassword"
|
||||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
inputType="password"
|
||||||
>{{ "settings.security.new-password" | translate }}</label
|
label="settings.security.new-password"
|
||||||
>
|
required="true"
|
||||||
<input
|
|
||||||
formControlName="newPassword"
|
|
||||||
type="password"
|
|
||||||
id="newPassword"
|
|
||||||
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
|
<shared-input
|
||||||
for="newPasswordConfirmation"
|
key="newPasswordConfirmation"
|
||||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
inputType="password"
|
||||||
>{{ "settings.security.password-confirmation" | translate }}</label
|
label="settings.security.password-confirmation"
|
||||||
>
|
required="true"
|
||||||
<input
|
|
||||||
formControlName="newPasswordConfirmation"
|
|
||||||
type="password"
|
|
||||||
id="newPasswordConfirmation"
|
|
||||||
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
|
<shared-submit
|
||||||
(click)="changePassword()"
|
label="settings.security.change-password"
|
||||||
[disabled]="!changePasswordForm.valid"
|
(onSubmit)="changePassword()"
|
||||||
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 }}
|
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h2>{{ "settings.security.change-username" | translate }}</h2>
|
<h2>{{ "settings.security.change-username" | translate }}</h2>
|
||||||
<form class="mx-auto p-5" [formGroup]="changeUsernameForm">
|
<form class="mx-auto py-5" [formGroup]="changeUsernameForm">
|
||||||
<div class="mb-5">
|
<div class="mb-5">
|
||||||
<label
|
<shared-input
|
||||||
for="password"
|
key="password"
|
||||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
inputType="password"
|
||||||
>{{ "settings.security.current-password" | translate }}</label
|
label="settings.security.current-password"
|
||||||
>
|
required="true"
|
||||||
<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
|
<shared-input
|
||||||
for="newUsername"
|
key="newUsername"
|
||||||
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
inputType="string"
|
||||||
>{{ "settings.security.new-username" | translate }}</label
|
label="settings.security.new-username"
|
||||||
>
|
required="true"
|
||||||
<input
|
|
||||||
formControlName="newUsername"
|
|
||||||
type="string"
|
|
||||||
id="newUsername"
|
|
||||||
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
|
<shared-submit
|
||||||
(click)="changeUsername()"
|
label="settings.security.change-username"
|
||||||
[disabled]="!changeUsernameForm.valid"
|
(onSubmit)="changeUsername()"
|
||||||
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 }}
|
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -22,6 +22,14 @@ export class TabSecurityComponent {
|
|||||||
constructor(private authService: AuthService) {}
|
constructor(private authService: AuthService) {}
|
||||||
|
|
||||||
changePassword(): void {
|
changePassword(): void {
|
||||||
|
console.log({
|
||||||
|
password: this.changePasswordForm.value.password!,
|
||||||
|
newPassword: this.changePasswordForm.value.newPassword!,
|
||||||
|
newPasswordConfirmation:
|
||||||
|
this.changePasswordForm.value.newPasswordConfirmation!,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
|
||||||
this.authService.changePassword({
|
this.authService.changePassword({
|
||||||
password: this.changePasswordForm.value.password!,
|
password: this.changePasswordForm.value.password!,
|
||||||
newPassword: this.changePasswordForm.value.newPassword!,
|
newPassword: this.changePasswordForm.value.newPassword!,
|
||||||
|
|||||||
27
src/app/shared/components/input/input.component.html
Normal file
27
src/app/shared/components/input/input.component.html
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<div>
|
||||||
|
<label
|
||||||
|
*ngIf="label !== undefined"
|
||||||
|
[for]="key"
|
||||||
|
class="block mb-2 text-sm font-medium text-skin-primary-muted"
|
||||||
|
>{{ label | translate }}</label
|
||||||
|
>
|
||||||
|
|
||||||
|
<input
|
||||||
|
*ngIf="required"
|
||||||
|
[id]="key"
|
||||||
|
[type]="inputType"
|
||||||
|
[formControlName]="key"
|
||||||
|
[placeholder]="placeholder"
|
||||||
|
class="bg-skin-primary border border-skin-primary text-skin-primary text-sm rounded-lg focus:ring-skin-accent focus:border-skin-accent block w-full p-2.5"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
*ngIf="!required"
|
||||||
|
[id]="key"
|
||||||
|
[type]="inputType"
|
||||||
|
[formControlName]="key"
|
||||||
|
[placeholder]="placeholder"
|
||||||
|
class="bg-skin-primary border border-skin-primary text-skin-primary text-sm rounded-lg focus:ring-skin-accent focus:border-skin-accent block w-full p-2.5"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
21
src/app/shared/components/input/input.component.ts
Normal file
21
src/app/shared/components/input/input.component.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Component, inject, Input } from "@angular/core";
|
||||||
|
import { ControlContainer, FormGroup } from "@angular/forms";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "shared-input",
|
||||||
|
templateUrl: "./input.component.html",
|
||||||
|
styleUrl: "./input.component.scss",
|
||||||
|
viewProviders: [
|
||||||
|
{
|
||||||
|
provide: ControlContainer,
|
||||||
|
useFactory: () => inject(ControlContainer, { skipSelf: true }),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class InputComponent {
|
||||||
|
@Input() label: string | undefined = undefined;
|
||||||
|
@Input() placeholder: string = "";
|
||||||
|
@Input() required: boolean = false;
|
||||||
|
@Input({ required: true }) inputType!: string;
|
||||||
|
@Input({ required: true }) key!: string;
|
||||||
|
}
|
||||||
8
src/app/shared/components/submit/submit.component.html
Normal file
8
src/app/shared/components/submit/submit.component.html
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<button
|
||||||
|
(click)="submit()"
|
||||||
|
[disabled]="!getParentFormGroup().valid"
|
||||||
|
type="submit"
|
||||||
|
class="w-full cursor-pointer 9xl:w-auto font-bold text-skin-primary bg-skin-accent hover:bg-skin-accent-muted focus:ring-2 focus:ring-skin-accent rounded-lg text-sm px-5 py-2.5 text-center"
|
||||||
|
>
|
||||||
|
{{ label | translate }}
|
||||||
|
</button>
|
||||||
34
src/app/shared/components/submit/submit.component.ts
Normal file
34
src/app/shared/components/submit/submit.component.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { Component, EventEmitter, inject, Input, Output } from "@angular/core";
|
||||||
|
import { ControlContainer, FormGroup } from "@angular/forms";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "shared-submit",
|
||||||
|
templateUrl: "./submit.component.html",
|
||||||
|
styleUrl: "./submit.component.scss",
|
||||||
|
viewProviders: [
|
||||||
|
{
|
||||||
|
provide: ControlContainer,
|
||||||
|
useFactory: () => inject(ControlContainer, { skipSelf: true }),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class SubmitComponent {
|
||||||
|
@Output() onSubmit = new EventEmitter();
|
||||||
|
@Input({ required: true }) label!: string;
|
||||||
|
|
||||||
|
constructor(private controlContainer: ControlContainer) {}
|
||||||
|
|
||||||
|
getParentFormGroup(): FormGroup {
|
||||||
|
return this.controlContainer.control as FormGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
submit(): void {
|
||||||
|
var formGroup = this.getParentFormGroup();
|
||||||
|
if (!formGroup.valid) {
|
||||||
|
console.log("formGroup not Valid");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onSubmit.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
<div class="">
|
||||||
|
<div class="mb-4 border-b border-skin-primary">
|
||||||
|
<ul
|
||||||
|
role="tablist"
|
||||||
|
class="flex flex-nowrap -mb-px text-sm font-medium text-center"
|
||||||
|
data-tabs-toggle="#tab-outlet"
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
*ngFor="let tab of tabs"
|
||||||
|
class="me-2 w-full xl:w-auto"
|
||||||
|
role="presentation"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
role="tab"
|
||||||
|
class="block p-4 w-full xl:w-auto border-b-2 rounded-t-lg"
|
||||||
|
[data-tabs-target]="tab.target"
|
||||||
|
>
|
||||||
|
{{ tab.label | translate }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div id="tab-outlet" class="text-skin-primary">
|
||||||
|
<ng-content />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import { Component, Input, OnInit } from "@angular/core";
|
||||||
|
import { initFlowbite } from "flowbite";
|
||||||
|
import { TabItem } from "../../models/tab-item.model";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "shared-tab-control",
|
||||||
|
templateUrl: "./tab-control.component.html",
|
||||||
|
styleUrl: "./tab-control.component.scss",
|
||||||
|
})
|
||||||
|
export class TabControlComponent implements OnInit {
|
||||||
|
@Input({ required: true }) tabs!: TabItem[];
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
initFlowbite();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,55 +1,57 @@
|
|||||||
<table class="w-full text-skin-primary">
|
<table class="w-full table-fixed text-skin-primary">
|
||||||
|
<thead
|
||||||
<thead class="uppercase bg-skin-accent border-b border-skin-primary text-left">
|
class="uppercase bg-skin-accent border-b border-skin-primary text-left"
|
||||||
|
>
|
||||||
<tr>
|
<tr>
|
||||||
<th
|
<th *ngFor="let column of columns" class="px-5 py-2">
|
||||||
*ngFor="let column of columns"
|
{{ column.label | translate }}
|
||||||
class="px-5 py-2"
|
|
||||||
>
|
|
||||||
{{column.header}}
|
|
||||||
</th>
|
</th>
|
||||||
|
<th *ngIf="rowActions.length > 0" class="px-5 py-2"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let item of items" class="border-b border-skin-primary">
|
<tr *ngFor="let item of items" class="border-b border-skin-primary">
|
||||||
|
|
||||||
<td
|
<td
|
||||||
*ngFor="let column of columns; index as currentIndex"
|
*ngFor="let column of columns; index as currentIndex"
|
||||||
class="px-5 py-2"
|
class="px-5 py-2 text-skin-primary"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'bg-skin-primary': currentIndex % 2 === 0,
|
'bg-skin-primary': currentIndex % 2 === 0,
|
||||||
'bg-skin-secondary': currentIndex % 2 === 1,
|
'bg-skin-secondary': currentIndex % 2 === 1,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div *ngIf="column.routerLink !== undefined">
|
<div *ngIf="column.routerLink !== undefined">
|
||||||
<a [routerLink]="column.routerLink(item)" class="font-bold hover:text-skin-accent hover:underline">
|
<a [routerLink]="getRouterLink(column, item)">
|
||||||
<div *ngIf="column.columnContent !== undefined">
|
<div class="text-skin-accent hover:underline">
|
||||||
{{ column.columnContent }}
|
{{ getContent(column, item) }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
*ngIf="column.columnFunction !== undefined"
|
|
||||||
[innerHtml]="column.columnFunction(item)"
|
|
||||||
></div>
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="column.routerLink === undefined">
|
<div *ngIf="column.routerLink === undefined">
|
||||||
<div *ngIf="column.columnContent !== undefined">
|
{{ getContent(column, item) }}
|
||||||
<div *ngIf="isBoolean(column.columnContent)">
|
|
||||||
<input type="checkbox" [(ngModel)]="column.columnContent" />
|
|
||||||
</div>
|
|
||||||
<div *ngIf="!isBoolean(column.columnContent)">
|
|
||||||
{{ column.columnContent }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
*ngIf="column.columnFunction !== undefined"
|
|
||||||
[innerHtml]="column.columnFunction(item)"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
<td
|
||||||
|
*ngIf="rowActions.length > 0"
|
||||||
|
class="text-right w-auto h-full"
|
||||||
|
[ngClass]="{
|
||||||
|
'bg-skin-primary': columns.length % 2 === 0,
|
||||||
|
'bg-skin-secondary': columns.length % 2 === 1,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="p-2 font-bold text-skin-primary-muted hover:text-skin-primary"
|
||||||
|
*ngFor="let rowAction of rowActions"
|
||||||
|
(click)="rowAction.action(item)"
|
||||||
|
>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6">
|
||||||
|
<title>{{ rowAction.label | translate }}</title>
|
||||||
|
<path fill="currentColor" [attr.d]="rowAction.path" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@ -1,10 +1,15 @@
|
|||||||
import { Component, Input } from "@angular/core";
|
import { Component, Input } from "@angular/core";
|
||||||
|
|
||||||
export interface ColumnDefinition {
|
export interface ColumnDefinition {
|
||||||
header: string;
|
label: string;
|
||||||
columnContent?: string | undefined;
|
content: Function | string | boolean | number;
|
||||||
columnFunction?: Function | undefined;
|
routerLink: Function | string | undefined;
|
||||||
routerLink?: Function | undefined;
|
}
|
||||||
|
|
||||||
|
export interface RowAction {
|
||||||
|
label: string;
|
||||||
|
action: Function;
|
||||||
|
path: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -15,8 +20,29 @@ export interface ColumnDefinition {
|
|||||||
export class TableComponent {
|
export class TableComponent {
|
||||||
@Input() items: any[] = [];
|
@Input() items: any[] = [];
|
||||||
@Input() columns: ColumnDefinition[] = [];
|
@Input() columns: ColumnDefinition[] = [];
|
||||||
|
@Input() rowActions: RowAction[] = [];
|
||||||
|
|
||||||
isBoolean(obje: any): boolean {
|
isBoolean(obje: any): boolean {
|
||||||
return typeof obje === "boolean";
|
return typeof obje === "boolean";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getRouterLink(column: ColumnDefinition, item: any) {
|
||||||
|
if (column.routerLink === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof column.routerLink === "string") {
|
||||||
|
return column.routerLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
return column.routerLink(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
getContent(column: ColumnDefinition, item: any) {
|
||||||
|
if (typeof column.content === "function") {
|
||||||
|
return column.content(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return column.content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
export interface TabItem {
|
export interface TabItem {
|
||||||
id: string;
|
label: string;
|
||||||
title: string;
|
target: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,11 +4,35 @@ 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, ReactiveFormsModule } from "@angular/forms";
|
||||||
|
import { InputComponent } from "./components/input/input.component";
|
||||||
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
|
import { SubmitComponent } from "./components/submit/submit.component";
|
||||||
|
import { TabControlComponent } from "./components/tab-control/tab-control.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [CardComponent, PaginatorComponent, TableComponent],
|
declarations: [
|
||||||
exports: [CardComponent, PaginatorComponent, TableComponent, FormsModule],
|
CardComponent,
|
||||||
imports: [RouterModule, CommonModule, FormsModule],
|
PaginatorComponent,
|
||||||
|
TableComponent,
|
||||||
|
TabControlComponent,
|
||||||
|
InputComponent,
|
||||||
|
SubmitComponent,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
CardComponent,
|
||||||
|
PaginatorComponent,
|
||||||
|
TableComponent,
|
||||||
|
TabControlComponent,
|
||||||
|
InputComponent,
|
||||||
|
SubmitComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
RouterModule,
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
TranslateModule,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class SharedModule {}
|
export class SharedModule {}
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
"login-now": "Jetzt anmelden!",
|
"login-now": "Jetzt anmelden!",
|
||||||
"register-now": "Jetzt registrieren!",
|
"register-now": "Jetzt registrieren!",
|
||||||
"forgot-password": "Passwort vergessen",
|
"forgot-password": "Passwort vergessen",
|
||||||
"confirm-registration": "Registrierung bestätigen"
|
"confirm-registration": "Registrierung abschließen"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"tab-security": "Sicherheit",
|
"tab-security": "Sicherheit",
|
||||||
@ -50,5 +50,13 @@
|
|||||||
"new-username": "Neuer Benutzername",
|
"new-username": "Neuer Benutzername",
|
||||||
"password-confirmation": "Passwort wiederholen"
|
"password-confirmation": "Passwort wiederholen"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"test": {
|
||||||
|
"product": "Produkt",
|
||||||
|
"price": "Preis",
|
||||||
|
"stock": "Lagerbestand",
|
||||||
|
"edit": "Bearbeiten",
|
||||||
|
"delete": "Löschen"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,5 +50,13 @@
|
|||||||
"new-username": "New Username",
|
"new-username": "New Username",
|
||||||
"password-confirmation": "Confirm Password"
|
"password-confirmation": "Confirm Password"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"test": {
|
||||||
|
"product": "Product",
|
||||||
|
"price": "Price",
|
||||||
|
"stock": "Stock",
|
||||||
|
"edit": "Edit",
|
||||||
|
"delete": "Delete"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,28 +3,41 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
|
/* Light Theme */
|
||||||
: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: 50, 50, 50;
|
||||||
--color-text-secondary: 255, 255, 255;
|
--color-text-secondary: 255, 255, 255;
|
||||||
--color-text-secondary-muted: 170, 170, 170;
|
--color-text-secondary-muted: 200, 200, 200;
|
||||||
--color-text-accent: 250, 183, 0;
|
|
||||||
--color-text-accent-muted: 250, 183, 0;
|
|
||||||
|
|
||||||
--color-primary: 244, 244, 245;
|
--color-text-accent: 255, 180, 0;
|
||||||
--color-secondary: 238, 238, 240;
|
--color-text-accent-muted: 240, 170, 0;
|
||||||
--color-accent: 255, 199, 44;
|
|
||||||
|
--color-background-primary: 255, 255, 255;
|
||||||
|
--color-background-primary-muted: 240, 240, 240;
|
||||||
|
--color-background-secondary: 245, 245, 245;
|
||||||
|
--color-background-secondary-muted: 230, 230, 230;
|
||||||
|
|
||||||
|
--color-background-accent: 255, 199, 44;
|
||||||
|
--color-background-accent-muted: 240, 184, 34;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dark Theme */
|
||||||
.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-muted: 250, 183, 0;
|
|
||||||
|
|
||||||
--color-primary: 24, 24, 27;
|
--color-text-accent: 255, 180, 50;
|
||||||
--color-secondary: 39, 39, 42;
|
--color-text-accent-muted: 230, 160, 40;
|
||||||
--color-accent: 250, 183, 0;
|
|
||||||
|
--color-background-primary: 24, 24, 27;
|
||||||
|
--color-background-primary-muted: 34, 34, 37;
|
||||||
|
--color-background-secondary: 39, 39, 42;
|
||||||
|
--color-background-secondary-muted: 49, 49, 52;
|
||||||
|
|
||||||
|
--color-background-accent: 250, 183, 0;
|
||||||
|
--color-background-accent-muted: 225, 158, 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,9 +37,12 @@ module.exports = {
|
|||||||
// "backgrounds"
|
// "backgrounds"
|
||||||
backgroundColor: {
|
backgroundColor: {
|
||||||
skin: {
|
skin: {
|
||||||
primary: withOpacity("--color-primary"),
|
primary: withOpacity("--color-background-primary"),
|
||||||
secondary: withOpacity("--color-secondary"),
|
"primary-muted": withOpacity("--color-background-primary-muted"),
|
||||||
accent: withOpacity("--color-accent"),
|
secondary: withOpacity("--color-background-secondary"),
|
||||||
|
"secondary-muted": withOpacity("--color-background-secondary-muted"),
|
||||||
|
accent: withOpacity("--color-background-accent"),
|
||||||
|
"accent-muted": withOpacity("--color-background-accent-muted"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -52,6 +55,7 @@ module.exports = {
|
|||||||
ringColor: {
|
ringColor: {
|
||||||
skin: {
|
skin: {
|
||||||
primary: withOpacity("--color-text-secondary-muted"),
|
primary: withOpacity("--color-text-secondary-muted"),
|
||||||
|
accent: withOpacity("--color-text-accent-muted"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
divideColor: {
|
divideColor: {
|
||||||
|
|||||||
Reference in New Issue
Block a user