Skip to content

Commit

Permalink
ui: various improvements. started translation support
Browse files Browse the repository at this point in the history
  • Loading branch information
ppacher committed Apr 16, 2024
1 parent 827e593 commit 1f769a5
Show file tree
Hide file tree
Showing 36 changed files with 656 additions and 208 deletions.
Binary file added docs/content/.vuepress/public/hero.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@simplewebauthn/browser": "^7.2.0",
"@tierklinik-dobersberg/angular": "^0.0.11",
"@tierklinik-dobersberg/apis": "^0.0.11",
"angular-l10n": "^16.0.0",
"blueimp-load-image": "^5.16.0",
"countries-list": "^2.6.1",
"cropperjs": "^1.5.13",
Expand Down
11 changes: 10 additions & 1 deletion ui/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import { TkdImageComponent } from './components/image';
import { TkdAvatarComponent } from './components/avatar';
import { TkdMenuModule } from './components/menu';
import { TkdSideNavComponent } from './components/navigation';
import { provideL10nIntl, provideL10nTranslation } from 'angular-l10n';
import { TranslationLoader, l10nConfig } from './l10n-config';

const loadConfigFactory = (client: HttpClient) => {
return () =>
Expand Down Expand Up @@ -92,7 +94,14 @@ const loadConfigFactory = (client: HttpClient) => {
provide: USER_SERVICE,
useFactory: userServiceFactory,
deps: [TRANSPORT]
}
},
provideL10nTranslation(
l10nConfig,
{
translationLoader: TranslationLoader
}
),
provideL10nIntl(),
],
bootstrap: [AppComponent],
})
Expand Down
16 changes: 16 additions & 0 deletions ui/src/app/components/backlink/backlink.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Directive } from '@angular/core';
import { Location } from "@angular/common";
import { HostListener, inject } from "@angular/core";

@Directive({
selector: '[tkd-backlink]',
standalone: true,
})
export class TkdBacklinkDirective {
private readonly location = inject(Location);

@HostListener('click')
goBack() {
this.location.back();
}
}
1 change: 1 addition & 0 deletions ui/src/app/components/backlink/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './backlink.directive';
4 changes: 2 additions & 2 deletions ui/src/app/components/button/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class TkdButtonDirective implements OnChanges {
private readonly cdr = inject(ChangeDetectorRef);

@Input('tkd-button')
tkdButtonType: TkdButtonType = 'tertiary';
tkdButtonType: TkdButtonType | '' = 'secondary';

@Input('tkdSize')
tkdSize: TkdButtonSize = 'default';
Expand All @@ -46,7 +46,7 @@ export class TkdButtonDirective implements OnChanges {
commonClasses
];

classes.push(buttonMap[this.tkdButtonType])
classes.push(buttonMap[this.tkdButtonType|| 'secondary'])
classes.push(sizeMap[this.tkdSize]);

this.classList = classes.join(' ');
Expand Down
32 changes: 32 additions & 0 deletions ui/src/app/l10n-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Injectable } from "@angular/core";
import { L10nConfig, L10nProvider, L10nTranslationLoader } from "angular-l10n";
import { Observable, from } from "rxjs";

export const l10nConfig: L10nConfig = {
format: 'language-region',
providers: [
{ name: 'app', asset: 'app' }
],
cache: true,
keySeparator: '.',
defaultLocale: {
language: 'de-AT', currency: 'EUR', timeZone: 'Europe/Vienna'
},
schema: [
{
locale: {
language: 'de-AT', currency: 'EUR', timeZone: 'Europe/Vienna'
}
}
]
}

@Injectable()
export class TranslationLoader implements L10nTranslationLoader {
get(language: string, provider: L10nProvider): Observable<{ [key: string]: any; }> {
const data = import(`../i18n/${language}/${provider.asset}.json`)

return from(data);
}
}

68 changes: 54 additions & 14 deletions ui/src/app/pages/add-edit-address/add-edit-address.component.html
Original file line number Diff line number Diff line change
@@ -1,54 +1,94 @@
<div class="tkd-card">
<header class="flex !flex-row w-full items-center">
<a routerLink="/profile">
<a tkd-backlink>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" />
</svg>
</a>

<h1 class="flex-grow">{{ isNew ? 'Adresse hinzufügen' : 'Adresse bearbeiten'}}</h1>
<h1 class="flex-grow">{{ (isNew ? 'address.addNew' : 'address.edit') | translateAsync }}</h1>
</header>

<content>
<section>
<form (ngSubmit)="save()">
<form [formGroup]="form" (ngSubmit)="save()">
<div class="flex flex-col gap-4">
<div class="flex flex-row gap-4">
<!-- City Code -->
<div class="flex flex-col gap-0.5 w-36">
<span class="text-xs text-gray-500 uppercase dark:text-emerald-200 dark:text-opacity-70">PLZ</span>
<input class="w-full tkd-input" type="text" name="displayName" [formControl]="cityCode" placeholder="PLZ">
<span class="text-xs text-gray-500 uppercase dark:text-emerald-200 dark:text-opacity-70" [ngClass]="{
'text-red-600': cityCode.invalid && cityCode.dirty
}">
{{ 'address.inputs.cityCode.label' | translateAsync }} *
</span>
<input class="w-full tkd-input" type="text" name="cityCode" formControlName="cityCode"
[attr.placeholder]="'address.inputs.cityCode.placeholder' | translateAsync">
</div>

<!-- City Name -->
<div class="flex flex-col flex-grow gap-0.5 w-full">
<span class="text-xs text-gray-500 uppercase dark:text-emerald-200 dark:text-opacity-70">Ort</span>
<input class="w-full tkd-input" type="text" name="displayName" [formControl]="cityName" placeholder="Ort">
<span class="text-xs text-gray-500 uppercase dark:text-emerald-200 dark:text-opacity-70" [ngClass]="{
'text-red-600': cityName.invalid && cityName.dirty
}">
{{ 'address.inputs.cityName.label' | translateAsync }} *
</span>
<input class="w-full tkd-input" type="text" name="cityName" formControlName="cityName"
[attr.placeholder]="'address.inputs.cityName.placeholder' | translateAsync">
</div>
</div>

<!-- Street -->
<div class="flex flex-col gap-0.5 w-full">
<span class="text-xs text-gray-500 uppercase dark:text-emerald-200 dark:text-opacity-70">Straße</span>
<input class="w-full tkd-input" type="text" name="displayName" [formControl]="street" placeholder="Straße">
<span class="text-xs text-gray-500 uppercase dark:text-emerald-200 dark:text-opacity-70" [ngClass]="{
'text-red-600': street.invalid && street.dirty
}">
{{ 'address.inputs.street.label' | translateAsync }} *
</span>
<input class="w-full tkd-input" type="text" name="street" formControlName="street"
[attr.placeholder]="'address.inputs.street.placeholder' | translateAsync">
</div>

<!-- Extra -->
<div class="flex flex-col gap-0.5 w-full">
<span class="text-xs text-gray-500 uppercase dark:text-emerald-200 dark:text-opacity-70">Zusatz</span>
<input class="w-full tkd-input" type="text" name="displayName" [formControl]="extra" placeholder="Zusatz">
<span class="text-xs text-gray-500 uppercase dark:text-emerald-200 dark:text-opacity-70">
{{ 'address.inputs.extra.label' | translateAsync }}
</span>
<input class="w-full tkd-input" type="text" name="extra" formControlName="extra"
[attr.placeholder]="'address.inputs.extra.placeholder' | translateAsync">
</div>

<button type="submit" class="tkd-btn">Speichern</button>
<button *ngIf="!isNew" type="button" class="tkd-btn tkd-danger" (click)="deleteAddress()">Löschen</button>
<!-- Buttons -->
<div class="flex flex-row justify-between items-center self-end gap-4">
<button type="submit" tkd-button="primary" [disabled]="form.invalid">
{{ 'common.save' | translateAsync }}
</button>

<button *ngIf="!isNew" type="button" tkd-button="secondary" (click)="deleteAddress()">
{{ 'common.delete' | translateAsync }}
</button>
</div>
</div>
</form>
</section>

<!-- Required Field help text -->
<section>
<span class="text-sm">* {{ 'common.requiredFields' | translateAsync }}</span>
</section>

<!-- Save errors -->
<section *ngIf="saveAddrError" class="flex !flex-row gap-4 !items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-8 h-8 text-red-500 dark:text-red-300">
<path stroke-linecap="round" stroke-linejoin="round"
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
</svg>
<span class="text-red-500 dark:text-red-300">
Adresse konnte nicht gespeichert werden: <br />
{{ 'address.saveError' | translateAsync }} <br />
{{ saveAddrError }}
</span>
</section>

</content>
</div>
67 changes: 44 additions & 23 deletions ui/src/app/pages/add-edit-address/add-edit-address.component.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { CommonModule } from '@angular/common';
import { CommonModule, Location } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, OnInit, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, RequiredValidator, Validators } from '@angular/forms';
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
import { ConnectError } from '@bufbuild/connect';
import { Address } from '@tierklinik-dobersberg/apis';
import { L10N_LOCALE, L10nTranslateAsyncPipe, L10nTranslatePipe } from 'angular-l10n';
import { combineLatest } from 'rxjs';
import { SELF_SERVICE } from 'src/app/clients';
import { TkdBacklinkDirective } from 'src/app/components/backlink';
import { TkdButtonDirective } from 'src/app/components/button';
import { ProfileService } from 'src/services/profile.service';

@Component({
Expand All @@ -16,7 +19,9 @@ import { ProfileService } from 'src/services/profile.service';
CommonModule,
FormsModule,
ReactiveFormsModule,
RouterModule,
TkdButtonDirective,
TkdBacklinkDirective,
L10nTranslateAsyncPipe
],
templateUrl: './add-edit-address.component.html',
styleUrls: ['./add-edit-address.component.css'],
Expand All @@ -25,21 +30,33 @@ import { ProfileService } from 'src/services/profile.service';
export class AddEditAddressComponent implements OnInit {
isNew = true;

profileService = inject(ProfileService);
selfService = inject(SELF_SERVICE);
activeRoute = inject(ActivatedRoute)
destroyRef = inject(DestroyRef);
router = inject(Router);
cdr = inject(ChangeDetectorRef);
private readonly profileService = inject(ProfileService);
private readonly selfService = inject(SELF_SERVICE);
private readonly activeRoute = inject(ActivatedRoute)
private readonly destroyRef = inject(DestroyRef);
private readonly location = inject(Location);
private readonly cdr = inject(ChangeDetectorRef);

saveAddrError: string | null = null;

id: string | null = null;
cityCode = new FormControl('');
cityName = new FormControl('');
countryName = new FormControl('');
street = new FormControl('');
extra = new FormControl('');

form = new FormGroup({
cityCode: new FormControl('', {
validators: Validators.required,
}),

cityName: new FormControl('', {
validators: Validators.required
}),

street: new FormControl('', {
validators: Validators.required
}),

extra: new FormControl('')
})


ngOnInit(): void {
combineLatest([
Expand All @@ -57,10 +74,11 @@ export class AddEditAddressComponent implements OnInit {
const address = (profile?.addresses || []).find(addr => addr.id === this.id);

if (!address) {
this.router.navigate(['../'])
this.location.back()
} else {
this.cityCode.setValue(address.cityCode);
this.cityName.setValue(address.cityName);

this.street.setValue(address.street);
this.extra.setValue(address.extra);

Expand All @@ -70,13 +88,13 @@ export class AddEditAddressComponent implements OnInit {
})
}

get cityCode() { return this.form.get('cityCode')! }
get cityName() { return this.form.get('cityName')! }
get street() { return this.form.get('street')! }
get extra() { return this.form.get('extra')! }

async save() {
const addr: Partial<Address> = {
cityCode: this.cityCode.value!,
cityName: this.cityName.value!,
street: this.street.value!,
extra: this.extra.value!,
};
const addr = this.form.value as Partial<Address>;

try {

Expand All @@ -87,12 +105,15 @@ export class AddEditAddressComponent implements OnInit {
if (this.cityCode.dirty) {
paths.push("city_code");
}

if (this.cityName.dirty) {
paths.push("city_name")
}

if (this.street.dirty) {
paths.push("street")
}

if (this.extra.dirty) {
paths.push("extra")
}
Expand All @@ -105,7 +126,7 @@ export class AddEditAddressComponent implements OnInit {
}

await this.profileService.loadProfile();
this.router.navigate(['../'])
this.location.back()

} catch (err) {
this.saveAddrError = ConnectError.from(err).rawMessage;
Expand All @@ -120,7 +141,7 @@ export class AddEditAddressComponent implements OnInit {

await this.selfService.deleteAddress({ id: this.id! })
await this.profileService.loadProfile();
this.router.navigate(['../'])
this.location.back()
}
}

Loading

0 comments on commit 1f769a5

Please sign in to comment.