mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merged PR 1564: Customer RD
This commit is contained in:
committed by
Nino Righi
parent
c9a90211ee
commit
5c9f4c5b21
@@ -930,6 +930,5 @@ export class DomainCheckoutService {
|
||||
private updateProcessCount(processId: number, count: number) {
|
||||
this.applicationService.patchProcessData(processId, { count });
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
@@ -18,14 +18,15 @@ import {
|
||||
NotificationChannel,
|
||||
PayerDTO,
|
||||
PayerService,
|
||||
QueryTokenDTO,
|
||||
ResponseArgsOfIEnumerableOfBonusCardInfoDTO,
|
||||
ShippingAddressDTO,
|
||||
ShippingAddressService,
|
||||
} from '@swagger/crm';
|
||||
import { isArray } from '@utils/common';
|
||||
import { isArray, memorize } from '@utils/common';
|
||||
import { PagedResult, Result } from 'apps/domain/defs/src/public-api';
|
||||
import { Observable, of, ReplaySubject } from 'rxjs';
|
||||
import { catchError, map, mergeMap, retry } from 'rxjs/operators';
|
||||
import { catchError, map, mergeMap, retry, shareReplay } from 'rxjs/operators';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CrmCustomerService {
|
||||
@@ -38,6 +39,14 @@ export class CrmCustomerService {
|
||||
private loyaltyCardService: LoyaltyCardService
|
||||
) {}
|
||||
|
||||
@memorize()
|
||||
filterSettings() {
|
||||
return this.customerService.CustomerCustomerQuerySettings().pipe(
|
||||
map((res) => res.result),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
complete(queryString: string, filter?: { [key: string]: string }): Observable<Result<AutocompleteDTO[]>> {
|
||||
return this.customerService.CustomerCustomerAutocomplete({
|
||||
input: queryString,
|
||||
@@ -66,6 +75,15 @@ export class CrmCustomerService {
|
||||
});
|
||||
}
|
||||
|
||||
getCustomersWithQueryToken(queryToken: QueryTokenDTO) {
|
||||
if (queryToken.skip === undefined) queryToken.skip = 0;
|
||||
if (queryToken.take === undefined) queryToken.take = 20;
|
||||
if (queryToken.input === undefined) queryToken.input = { qs: '' };
|
||||
if (queryToken.filter === undefined) queryToken.filter = {};
|
||||
|
||||
return this.customerService.CustomerListCustomers(queryToken);
|
||||
}
|
||||
|
||||
getCustomersByCustomerCardNumber(queryString: string): Observable<PagedResult<CustomerInfoDTO>> {
|
||||
return this.customerService.CustomerGetCustomerByBonuscard(!!queryString ? queryString : undefined);
|
||||
}
|
||||
|
||||
@@ -32,11 +32,11 @@ import { IsaErrorHandler } from './providers/isa.error-handler';
|
||||
import { ScanAdapterModule, ScanAdapterService, ScanditScanAdapterModule } from '@adapter/scan';
|
||||
import { RootStateService } from './store/root-state.service';
|
||||
import * as Commands from './commands';
|
||||
import { UiIconModule, UI_ICON_CFG } from '@ui/icon';
|
||||
import { PreviewComponent } from './preview';
|
||||
import { NativeContainerService } from 'native-container';
|
||||
import { ShellModule } from '@shared/shell';
|
||||
import { MainComponent } from './main.component';
|
||||
import { IconModule } from '@shared/components/icon';
|
||||
|
||||
registerLocaleData(localeDe, localeDeExtra);
|
||||
registerLocaleData(localeDe, 'de', localeDeExtra);
|
||||
@@ -106,7 +106,7 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
|
||||
ScanAdapterModule.forRoot(),
|
||||
ScanditScanAdapterModule.forRoot(),
|
||||
PlatformModule,
|
||||
UiIconModule.forRoot(),
|
||||
IconModule.forRoot(),
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
@@ -135,11 +135,6 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
|
||||
useClass: IsaErrorHandler,
|
||||
},
|
||||
{ provide: LOCALE_ID, useValue: 'de-DE' },
|
||||
{
|
||||
provide: UI_ICON_CFG,
|
||||
useFactory: (config: Config) => config.get('@ui/icon'),
|
||||
deps: [Config],
|
||||
},
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
|
||||
335
apps/isa-app/src/assets/icons.json
Normal file
335
apps/isa-app/src/assets/icons.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -69,5 +69,6 @@
|
||||
"checkForUpdates": 3600000,
|
||||
"licence": {
|
||||
"scandit": "AQZyKCc+BEkNL00Y3h3FjawGLF+INUj7cVb0My91hl8ffiW873T8FTV1k4TIZJx5RwcJlYxhgsxHVcnM4AJgSwJhbAfxJmP/3XGijLlLp3XUIRjQwFtf7UlZAFZ7Vrt1/WSf7kxxrFQ2SE2AQwLqPg9DL+hHEfd4xT/15n8p2q7qUlCKLsV6jF12Pd7koFNSWNL3ZIkRtd1ma99/321dnwAJHFGXqWg5nprJ7sYtqUqNQ8Er9SlvKbhnw3AipHzKpz0O3oNfUsr6NlZivRBhMhCZLo5WpXo1m9uIU8zLEWMNDJ+wGUctcGxE3eCptP2zLXUgxxjB+0EXOUtT/GWUc/Ip61CMiyUf7Paz026E2eYil2yWgfkTP5CUgDMNGZFuAA1T5PhB9FRW51CjAIvwOKVMCvfixJiVoUsXHnWH2ZnXqtbDR/uEZBE7OKoBlaPL4G3Lvgdqym5EjROAztUXb6wOmVDiGzzqgizyZnIcxFBSKJAownGj9Vh4/Y/Ag1xzGzNtjz3ngSRfMfIIq/q2Q51uiLiv7mBVliPvPWMUTfTjnqnK/OSBlR2ID+COJqnUKpQMedPyOT3IMznmM6gQCmyYO5KE0MkfhFh6+pdNi6oJM2iZsxK1Z1V+GRSOIwrJEoajjDJkh439XjXk8NExFvplrLjK/oL/dsHIZiG6U5GVWW92kGkuXkJCeUz1CET3paxbGqwrd53r5d6gFABbC12CtcP2JeH4YYCpHYyPQacf0prj9Hdq3wDztShC9tH+4UQS/GbaDHKcS1ANIyPuTxHmBFtPuCJ9Uagy5QBEc8eAz2nfsbfaUxYzco6u/zhNsFbqp6zgQIxs5OcqDQ=="
|
||||
}
|
||||
},
|
||||
"@shared/icon": "/assets/icons.json"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -70,5 +70,6 @@
|
||||
"checkForUpdates": 3600000,
|
||||
"licence": {
|
||||
"scandit": "AfHi/mY+RbwJD5nC7SuWn3I14pFUOfSbQ2QG//4aV3zWQjwix30kHqsqraA8ZiipDBql8YlwIyV6VPBMUiAX4s9YHDxHHsWwq2BUB3ImzDEcU1jmMH/5yakGUYpCQ68D0iZ8SG9sS0QBb3iFdCHc1r9DFr1cMTxM7zOvb/AUoIVmieHZXnx9ioUgCvczsLiuX3hwvTW3lhbvJ4uUyqTWK4sWFVwoY4AIWSFrPwwrkV2DksMKT5fMJT3GWgPypvTIGwWvpRfLWwKlc1Z3ckyb84khsnaWD2wr+hdgu/K8YIMmgGszm5KIZ/G05YfDNZtQ4jby+5RZvQwWR8rxM35rJgf73OkMSpuL9jw3T0TTAlvpkGRLzVVuCw9VjlBLqfPNEZ6VsEwFuAla9IYUvFHCsjypg2J6UpxHXrTYmbsSu5Jm8frVfS5znPPTO9D/4rF6ZVv2PxY9PgUgJUvwMa/VMc/nse3RRRf8RGT4rUItfJDFO8pujD76vVEWq/KixQRoMdLgDLyxhsFVftkxqhZhyEfFZzsEy49LSojJ28vpHpBWLeCQBmnZ7JZ4C5yOQiqSQV/assBq2zJN2q+vCDp8qy5j1rED1SX5Ec7JpgpgnU4chLIf5Zn7bP/hNGT3pEYBuXeDXXN8ke1pcc3fc3m0FysDG0o56XVCUqImZ8Ezi8eujZciKDrWbtljhKTj7cnfuJx0sVHF6Bh5i4YfgA/Z+NL+MtH2EVIF67e6hEz6PWYTcoh3ybBaJfxb2FNvGJutNKg04GwMhYq6K2IddBt0fDiBt0SGM0oSBlUP3DKCUmXcf2a6ASbrcqv6Wz1jHt0pY4U8bEpg7qSbW3VDyvdPgyQ="
|
||||
}
|
||||
},
|
||||
"@shared/icon": "/assets/icons.json"
|
||||
}
|
||||
@@ -70,5 +70,6 @@
|
||||
"checkForUpdates": 3600000,
|
||||
"licence": {
|
||||
"scandit": "AfHi/mY+RbwJD5nC7SuWn3I14pFUOfSbQ2QG//4aV3zWQjwix30kHqsqraA8ZiipDBql8YlwIyV6VPBMUiAX4s9YHDxHHsWwq2BUB3ImzDEcU1jmMH/5yakGUYpCQ68D0iZ8SG9sS0QBb3iFdCHc1r9DFr1cMTxM7zOvb/AUoIVmieHZXnx9ioUgCvczsLiuX3hwvTW3lhbvJ4uUyqTWK4sWFVwoY4AIWSFrPwwrkV2DksMKT5fMJT3GWgPypvTIGwWvpRfLWwKlc1Z3ckyb84khsnaWD2wr+hdgu/K8YIMmgGszm5KIZ/G05YfDNZtQ4jby+5RZvQwWR8rxM35rJgf73OkMSpuL9jw3T0TTAlvpkGRLzVVuCw9VjlBLqfPNEZ6VsEwFuAla9IYUvFHCsjypg2J6UpxHXrTYmbsSu5Jm8frVfS5znPPTO9D/4rF6ZVv2PxY9PgUgJUvwMa/VMc/nse3RRRf8RGT4rUItfJDFO8pujD76vVEWq/KixQRoMdLgDLyxhsFVftkxqhZhyEfFZzsEy49LSojJ28vpHpBWLeCQBmnZ7JZ4C5yOQiqSQV/assBq2zJN2q+vCDp8qy5j1rED1SX5Ec7JpgpgnU4chLIf5Zn7bP/hNGT3pEYBuXeDXXN8ke1pcc3fc3m0FysDG0o56XVCUqImZ8Ezi8eujZciKDrWbtljhKTj7cnfuJx0sVHF6Bh5i4YfgA/Z+NL+MtH2EVIF67e6hEz6PWYTcoh3ybBaJfxb2FNvGJutNKg04GwMhYq6K2IddBt0fDiBt0SGM0oSBlUP3DKCUmXcf2a6ASbrcqv6Wz1jHt0pY4U8bEpg7qSbW3VDyvdPgyQ="
|
||||
}
|
||||
},
|
||||
"@shared/icon": "/assets/icons.json"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,4 @@
|
||||
export const environment = {
|
||||
production: true,
|
||||
debug: false,
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
export const environment = {
|
||||
production: false,
|
||||
debug: false,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -14,26 +14,28 @@ if (environment.production) {
|
||||
|
||||
const debugService = new DebugService();
|
||||
|
||||
const consoleLog = console.log;
|
||||
if (environment.debug) {
|
||||
const consoleLog = console.log;
|
||||
|
||||
console.log = (...args) => {
|
||||
debugService.add({ type: 'log', args });
|
||||
consoleLog(...args);
|
||||
};
|
||||
console.log = (...args) => {
|
||||
debugService.add({ type: 'log', args });
|
||||
consoleLog(...args);
|
||||
};
|
||||
|
||||
const consoleWarn = console.warn;
|
||||
const consoleWarn = console.warn;
|
||||
|
||||
console.warn = (...args) => {
|
||||
debugService.add({ type: 'warn', args });
|
||||
consoleWarn(...args);
|
||||
};
|
||||
console.warn = (...args) => {
|
||||
debugService.add({ type: 'warn', args });
|
||||
consoleWarn(...args);
|
||||
};
|
||||
|
||||
const consoleError = console.error;
|
||||
const consoleError = console.error;
|
||||
|
||||
console.error = (...args) => {
|
||||
debugService.add({ type: 'error', args });
|
||||
consoleError(...args);
|
||||
};
|
||||
console.error = (...args) => {
|
||||
debugService.add({ type: 'error', args });
|
||||
consoleError(...args);
|
||||
};
|
||||
}
|
||||
|
||||
platformBrowserDynamic([{ provide: DebugService, useValue: debugService }])
|
||||
.bootstrapModule(AppModule)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
class="absolute right-0 top-0 h-14 rounded px-5 text-lg bg-cadet-blue flex flex-row flex-nowrap items-center justify-center"
|
||||
type="button"
|
||||
>
|
||||
<ui-svg-icon class="mr-2" icon="filter-variant"></ui-svg-icon>
|
||||
<shared-icon class="mr-2" icon="filter-variant"></shared-icon>
|
||||
Filter
|
||||
</button>
|
||||
</div>
|
||||
@@ -16,7 +16,7 @@
|
||||
<shell-filter-overlay #filterOverlay class="relative">
|
||||
<div class="relative">
|
||||
<button type="button" class="absolute top-4 right-4 text-cadet" (click)="closeFilterOverlay()">
|
||||
<ui-svg-icon [icon]="'close'" [size]="28"></ui-svg-icon>
|
||||
<shared-icon [icon]="'close'" [size]="28"></shared-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@ import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { SharedFilterOverlayModule } from '@shared/components/filter-overlay';
|
||||
import { UiFilterNextModule } from '@ui/filter';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
import { PriceUpdateListModule } from './price-update-list';
|
||||
import { PriceUpdateComponent } from './price-update.component';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, PriceUpdateListModule, UiIconModule, UiFilterNextModule, SharedFilterOverlayModule, UiSpinnerModule],
|
||||
imports: [CommonModule, PriceUpdateListModule, UiFilterNextModule, SharedFilterOverlayModule, UiSpinnerModule, IconComponent],
|
||||
exports: [PriceUpdateComponent],
|
||||
declarations: [PriceUpdateComponent],
|
||||
providers: [],
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
[routerLink]="closeFilterRoute"
|
||||
queryParamsHandling="preserve"
|
||||
>
|
||||
<ui-svg-icon icon="close" [size]="25"></ui-svg-icon>
|
||||
<shared-icon icon="close" [size]="25"></shared-icon>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
import { ArticleSearchFilterComponent } from './search-filter.component';
|
||||
import { FilterNextModule } from 'apps/shared/components/filter/src/lib';
|
||||
import { FilterModule } from '@shared/components/filter';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, RouterModule, FilterNextModule, UiIconModule, UiSpinnerModule],
|
||||
imports: [CommonModule, RouterModule, FilterModule, UiSpinnerModule, IconComponent],
|
||||
exports: [ArticleSearchFilterComponent],
|
||||
declarations: [ArticleSearchFilterComponent],
|
||||
providers: [],
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
[routerLink]="openFilterRoute"
|
||||
queryParamsHandling="preserve"
|
||||
>
|
||||
<ui-svg-icon class="mr-2" icon="filter-variant"></ui-svg-icon>
|
||||
<shared-icon class="mr-2" icon="filter-variant"></shared-icon>
|
||||
Filter
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { ArticleSearchMainComponent } from './search-main.component';
|
||||
import { FilterNextModule } from 'apps/shared/components/filter/src/lib';
|
||||
import { FilterModule } from '@shared/components/filter';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, RouterModule, UiIconModule, FilterNextModule],
|
||||
imports: [CommonModule, RouterModule, IconComponent, FilterModule, UiIconModule],
|
||||
exports: [ArticleSearchMainComponent],
|
||||
declarations: [ArticleSearchMainComponent],
|
||||
providers: [],
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
[routerLink]="filterRoute"
|
||||
queryParamsHandling="preserve"
|
||||
>
|
||||
<ui-svg-icon class="mr-2" icon="filter-variant"></ui-svg-icon>
|
||||
<shared-icon class="mr-2" icon="filter-variant"></shared-icon>
|
||||
Filter
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -15,9 +15,10 @@ import { SearchResultItemLoadingComponent } from './search-result-item-loading.c
|
||||
import { SearchResultItemComponent } from './search-result-item.component';
|
||||
import { ArticleSearchResultsComponent } from './search-results.component';
|
||||
import { SearchResultSelectedPipe } from './selected/search-result-selected.pipe';
|
||||
import { FilterAutocompleteProvider, FilterNextModule, OrderByFilterModule } from 'apps/shared/components/filter/src/lib';
|
||||
import { FilterAutocompleteProvider, FilterModule, OrderByFilterModule } from '@shared/components/filter';
|
||||
import { FocusSearchboxEvent } from '../focus-searchbox.event';
|
||||
import { ArticleSearchMainAutocompleteProvider } from '../providers';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -32,7 +33,8 @@ import { ArticleSearchMainAutocompleteProvider } from '../providers';
|
||||
OrderByFilterModule,
|
||||
ScrollingModule,
|
||||
UiTooltipModule,
|
||||
FilterNextModule,
|
||||
FilterModule,
|
||||
IconComponent,
|
||||
],
|
||||
exports: [ArticleSearchResultsComponent, SearchResultItemComponent],
|
||||
declarations: [
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[routerLink]="filterRoute"
|
||||
queryParamsHandling="preserve"
|
||||
>
|
||||
<ui-svg-icon class="mr-2" icon="filter-variant"></ui-svg-icon>
|
||||
<shared-icon class="mr-2" icon="filter-variant"></shared-icon>
|
||||
Filter
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { UiFilterNextModule } from '@ui/filter';
|
||||
import { SharedFilterOverlayModule } from '@shared/components/filter-overlay';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
import { CustomerOrderSearchStore } from './customer-order-search.store';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -19,6 +20,7 @@ import { CustomerOrderSearchStore } from './customer-order-search.store';
|
||||
SharedFilterOverlayModule,
|
||||
UiSpinnerModule,
|
||||
OrderBranchIdInputComponent,
|
||||
IconComponent,
|
||||
],
|
||||
exports: [CustomerOrderSearchComponent],
|
||||
providers: [CustomerOrderSearchStore],
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[routerLink]="filterRoute"
|
||||
queryParamsHandling="preserve"
|
||||
>
|
||||
<ui-svg-icon class="mr-2" icon="filter-variant"></ui-svg-icon>
|
||||
<shared-icon class="mr-2" icon="filter-variant"></shared-icon>
|
||||
Filter
|
||||
</a> -->
|
||||
|
||||
|
||||
7
apps/page/customer-rd/ng-package.json
Normal file
7
apps/page/customer-rd/ng-package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/page/customer",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply grid grid-flow-row gap-[5px] h-[6.125rem] bg-surface text-surface-content overflow-hidden mb-px-2 px-4 py-3;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="isa-label">
|
||||
{{ label?.description }}
|
||||
</div>
|
||||
<div class="mr-20">
|
||||
{{ customer?.created | date: 'dd.MM.yy' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<span class="text-[22px] font-bold"> {{ customer?.lastName }} {{ customer?.firstName }} </span>
|
||||
<div>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-row items-center">
|
||||
<span class="w-32">PLZ und Ort</span>
|
||||
<span class="font-bold grow-1">{{ customer?.address?.zipCode }} {{ customer?.address?.city }}</span>
|
||||
</div>
|
||||
<div class="flex flex-row items-center">
|
||||
<span class="w-32">E-Mail</span>
|
||||
<span class="font-bold grow-1">{{ customer?.communicationDetails?.email }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||
import { CustomerInfoDTO } from '@swagger/crm';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-result-list-item-full',
|
||||
templateUrl: 'customer-result-list-item-full.component.html',
|
||||
styleUrls: ['customer-result-list-item-full.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerResultListItemFullComponent {
|
||||
get label() {
|
||||
return this.customer?.features?.find((f) => f.enabled);
|
||||
}
|
||||
|
||||
@Input()
|
||||
customer: CustomerInfoDTO;
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply flex flex-col bg-surface text-surface-content h-[11.313rem] mb-[0.625rem] p-4;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="isa-label">
|
||||
{{ label?.description }}
|
||||
</div>
|
||||
<div>
|
||||
{{ customer?.created | date: 'dd.MM.yy' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col justify-between grow">
|
||||
<span class="text-[22px] font-bold"> {{ customer?.lastName }} {{ customer?.firstName }} </span>
|
||||
<div>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-row items-center">
|
||||
<span class="w-32">PLZ und Ort</span>
|
||||
<span class="font-bold grow-1">{{ customer?.address?.zipCode }} {{ customer?.address?.city }}</span>
|
||||
</div>
|
||||
<div class="flex flex-row items-center">
|
||||
<span class="w-32">E-Mail</span>
|
||||
<span class="font-bold grow-1">{{ customer?.communicationDetails?.email }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||
import { CustomerInfoDTO } from '@swagger/crm';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-result-list-item',
|
||||
templateUrl: 'customer-result-list-item.component.html',
|
||||
styleUrls: ['customer-result-list-item.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerResultListItemComponent {
|
||||
get label() {
|
||||
return this.customer?.features?.find((f) => f.enabled);
|
||||
}
|
||||
|
||||
@Input()
|
||||
customer: CustomerInfoDTO;
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
:host {
|
||||
--shadow: 0px 0px 10px rgba(220, 226, 233, 0.5);
|
||||
--border-radius: 0.313rem;
|
||||
@apply grid grid-flow-row;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.customer-result-list-header {
|
||||
box-shadow: var(--shadow);
|
||||
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
||||
}
|
||||
|
||||
page-customer-result-list-item-full,
|
||||
page-customer-result-list-item {
|
||||
cursor: pointer;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<cdk-virtual-scroll-viewport itemSize="98" class="h-[calc(100vh-18.875rem)]" *ngIf="!compact">
|
||||
<a
|
||||
*cdkVirtualFor="let customer of customers$ | async; trackBy: trackByFn"
|
||||
[routerLink]="customerSearchNavigation.detailsRoute({ processId: processId, customerId: customer.id })?.path"
|
||||
[queryParams]="customerSearchNavigation.detailsRoute({ processId: processId, customerId: customer.id })?.queryParams"
|
||||
>
|
||||
<page-customer-result-list-item-full [customer]="customer"></page-customer-result-list-item-full>
|
||||
</a>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
|
||||
<cdk-virtual-scroll-viewport itemSize="98" class="h-[calc(100vh-20.75rem)]" *ngIf="compact">
|
||||
<a
|
||||
*cdkVirtualFor="let customer of customers$ | async; trackBy: trackByFn"
|
||||
[routerLink]="customerSearchNavigation.detailsRoute({ processId: processId, customerId: customer.id })?.path"
|
||||
[queryParams]="customerSearchNavigation.detailsRoute({ processId: processId, customerId: customer.id })?.queryParams"
|
||||
>
|
||||
<page-customer-result-list-item [customer]="customer"></page-customer-result-list-item>
|
||||
</a>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Component, ChangeDetectionStrategy, Input, EventEmitter, Output } from '@angular/core';
|
||||
import { CustomerInfoDTO } from '@swagger/crm';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { BooleanInput, NumberInput, coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
|
||||
import { CustomerSearchNavigation } from '../../navigations';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-result-list',
|
||||
templateUrl: 'customer-result-list.component.html',
|
||||
styleUrls: ['customer-result-list.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerResultListComponent {
|
||||
private _compact: boolean;
|
||||
@Input()
|
||||
get compact() {
|
||||
return this._compact;
|
||||
}
|
||||
set compact(value: BooleanInput) {
|
||||
this._compact = coerceBooleanProperty(value);
|
||||
}
|
||||
|
||||
customers$ = this._customerService.getCustomers('Lorenz Hilpert').pipe(map((res) => res.result));
|
||||
|
||||
@Input()
|
||||
selected: CustomerInfoDTO;
|
||||
|
||||
@Output()
|
||||
selectedChange = new EventEmitter<CustomerInfoDTO>();
|
||||
|
||||
private _processId: NumberInput;
|
||||
@Input()
|
||||
get processId() {
|
||||
return this._processId;
|
||||
}
|
||||
set processId(value: NumberInput) {
|
||||
this._processId = coerceNumberProperty(value);
|
||||
}
|
||||
|
||||
trackByFn = (_: number, item: CustomerInfoDTO) => item?.id;
|
||||
|
||||
constructor(private _customerService: CrmCustomerService, public customerSearchNavigation: CustomerSearchNavigation) {}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
|
||||
import { SearchboxModule } from '@shared/components/searchbox';
|
||||
|
||||
import { CustomerResultListComponent } from './customer-result-list.component';
|
||||
import { CustomerResultListItemFullComponent } from './customer-result-list-item-full/customer-result-list-item-full.component';
|
||||
import { CustomerResultListItemComponent } from './customer-result-list-item/customer-result-list-item.component';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, SearchboxModule, ScrollingModule, RouterModule],
|
||||
exports: [CustomerResultListComponent, CustomerResultListComponent, CustomerResultListItemFullComponent, CustomerResultListItemComponent],
|
||||
declarations: [
|
||||
CustomerResultListComponent,
|
||||
CustomerResultListComponent,
|
||||
CustomerResultListItemFullComponent,
|
||||
CustomerResultListItemComponent,
|
||||
],
|
||||
})
|
||||
export class CustomerResultListModule {}
|
||||
@@ -0,0 +1,19 @@
|
||||
<shared-checkbox
|
||||
*ngIf="customerType !== 'b2b'"
|
||||
[ngModel]="p4mUser"
|
||||
(ngModelChange)="setValue({ p4mUser: !p4mUser })"
|
||||
[disabled]="p4mReadonly || readonly"
|
||||
>
|
||||
Kundenkarte
|
||||
</shared-checkbox>
|
||||
<ng-container *ngFor="let option of filteredOptions$ | async">
|
||||
<shared-checkbox
|
||||
*ngIf="option?.enabled !== false"
|
||||
[ngModel]="option.value === customerType"
|
||||
(ngModelChange)="setValue({ customerType: $event ? option.value : undefined })"
|
||||
[disabled]="readonly"
|
||||
[name]="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</shared-checkbox>
|
||||
</ng-container>
|
||||
@@ -0,0 +1,11 @@
|
||||
:host {
|
||||
@apply flex flex-row flex-wrap justify-start items-center;
|
||||
}
|
||||
|
||||
shared-checkbox {
|
||||
@apply mr-10 mb-5;
|
||||
}
|
||||
|
||||
shared-checkbox.disabled.checked {
|
||||
@apply text-black;
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
forwardRef,
|
||||
OnInit,
|
||||
Input,
|
||||
ChangeDetectorRef,
|
||||
OnDestroy,
|
||||
Output,
|
||||
EventEmitter,
|
||||
ViewChildren,
|
||||
QueryList,
|
||||
} from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { CacheService } from '@core/cache';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { ComponentStore, tapResponse } from '@ngrx/component-store';
|
||||
import { OptionDTO } from '@swagger/checkout';
|
||||
import { UiCheckboxComponent } from '@ui/checkbox';
|
||||
import { first, isBoolean, isString } from 'lodash';
|
||||
import { combineLatest, Observable, Subject } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
|
||||
export interface CustomerTypeSelectorState {
|
||||
processId: number;
|
||||
customerType: string;
|
||||
p4mUser: boolean;
|
||||
options: OptionDTO[];
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-customer-type-selector',
|
||||
templateUrl: 'customer-type-selector.component.html',
|
||||
styleUrls: ['customer-type-selector.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => CustomerTypeSelectorComponent),
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class CustomerTypeSelectorComponent extends ComponentStore<CustomerTypeSelectorState>
|
||||
implements OnInit, OnDestroy, ControlValueAccessor {
|
||||
@ViewChildren(UiCheckboxComponent)
|
||||
checkboxes: QueryList<UiCheckboxComponent>;
|
||||
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
@Input()
|
||||
readonly: boolean;
|
||||
|
||||
@Input()
|
||||
get value() {
|
||||
if (this.p4mUser) {
|
||||
return `${this.customerType}-p4m`;
|
||||
}
|
||||
return this.customerType;
|
||||
}
|
||||
set value(value: string) {
|
||||
if (value.includes('-p4m')) {
|
||||
this.p4mUser = true;
|
||||
this.customerType = value.replace('-p4m', '');
|
||||
} else {
|
||||
this.customerType = value;
|
||||
}
|
||||
}
|
||||
|
||||
@Output()
|
||||
valueChanges = new EventEmitter<string>();
|
||||
|
||||
@Input()
|
||||
disabled: boolean;
|
||||
|
||||
@Input()
|
||||
set processId(val: number) {
|
||||
if (this.processId !== val) {
|
||||
this.patchState({ processId: val });
|
||||
this.getOptions(val);
|
||||
}
|
||||
}
|
||||
get processId() {
|
||||
return this.get((s) => s.processId);
|
||||
}
|
||||
|
||||
@Input()
|
||||
get p4mUser() {
|
||||
return this.get((s) => s.p4mUser);
|
||||
}
|
||||
set p4mUser(val: boolean) {
|
||||
this.patchState({ p4mUser: val ?? false });
|
||||
}
|
||||
|
||||
@Input()
|
||||
get customerType() {
|
||||
return this.get((s) => s.customerType);
|
||||
}
|
||||
set customerType(val: string) {
|
||||
this.patchState({ customerType: val });
|
||||
}
|
||||
|
||||
@Input()
|
||||
p4mReadonly = false;
|
||||
|
||||
get filteredOptions$() {
|
||||
const options$ = this.select((s) => s.options).pipe(distinctUntilChanged());
|
||||
const p4mUser$ = this.select((s) => s.p4mUser).pipe(distinctUntilChanged());
|
||||
const customerType$ = this.select((s) => s.customerType).pipe(distinctUntilChanged());
|
||||
return combineLatest([options$, p4mUser$, customerType$]).pipe(
|
||||
filter(([options]) => options?.length > 0),
|
||||
map(([options, p4mUser, customerType]) => {
|
||||
const initial = { p4mUser: this.p4mUser, customerType: this.customerType };
|
||||
let result: OptionDTO[] = options;
|
||||
if (p4mUser) {
|
||||
result = result.filter((o) => o.value === 'store' || (o.value === 'webshop' && o.enabled !== false));
|
||||
|
||||
result = result.map((o) => {
|
||||
if (o.value === 'store') {
|
||||
return { ...o, enabled: false };
|
||||
}
|
||||
return o;
|
||||
});
|
||||
}
|
||||
|
||||
if (customerType === 'b2b' && this.p4mUser) {
|
||||
this.p4mUser = false;
|
||||
}
|
||||
|
||||
if (initial.p4mUser !== this.p4mUser || initial.customerType !== this.customerType) {
|
||||
this.setValue({ customerType: this.customerType, p4mUser: this.p4mUser });
|
||||
}
|
||||
|
||||
return result;
|
||||
}),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
get enabledOptions() {
|
||||
return this.get((s) => s.options.filter((o) => o.enabled !== false));
|
||||
}
|
||||
|
||||
onChange = (value: string) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _checkoutService: DomainCheckoutService, private _cache: CacheService, private _cdr: ChangeDetectorRef) {
|
||||
super({
|
||||
processId: undefined,
|
||||
customerType: undefined,
|
||||
p4mUser: false,
|
||||
options: _cache.get('customerTypeOptions') ?? [],
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
getOptions = this.effect((processId$: Observable<number>) => {
|
||||
return processId$.pipe(
|
||||
switchMap((pid) =>
|
||||
this._checkoutService.canSetCustomer({ processId: pid }).pipe(
|
||||
tapResponse(
|
||||
(res) => {
|
||||
const options = res.create.options.values;
|
||||
this.patchState({ options });
|
||||
this._cache.set('customerTypeOptions', options);
|
||||
},
|
||||
(err) => {}
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._onDestroy$.next();
|
||||
this._onDestroy$.complete();
|
||||
}
|
||||
|
||||
writeValue(obj: any): void {
|
||||
this.value = obj;
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
this.disabled = isDisabled;
|
||||
}
|
||||
|
||||
setValue(value: { p4mUser?: boolean; customerType?: string } | string) {
|
||||
const initial = { p4mUser: this.p4mUser, customerType: this.customerType };
|
||||
|
||||
if (isString(value)) {
|
||||
this.value = value;
|
||||
} else {
|
||||
if (isBoolean(value.p4mUser)) {
|
||||
this.p4mUser = value.p4mUser;
|
||||
}
|
||||
if (isString(value.customerType)) {
|
||||
this.customerType = value.customerType;
|
||||
} else if (this.p4mUser) {
|
||||
// Implementierung wie im PBI #3467 beschrieben
|
||||
// wenn customerType nicht gesetzt wird und p4mUser true ist,
|
||||
// dann customerType auf store setzen.
|
||||
// wenn dies nicht möglich ist da der Warenkob keinen store Kunden zulässt,
|
||||
// dann customerType auf webshop setzen.
|
||||
// wenn dies nicht möglich ist da der Warenkob keinen webshop Kunden zulässt,
|
||||
// dann customerType auf den ersten verfügbaren setzen und p4mUser auf false setzen.
|
||||
if (this.enabledOptions.some((o) => o.value === 'store')) {
|
||||
this.customerType = 'store';
|
||||
} else if (this.enabledOptions.some((o) => o.value === 'webshop')) {
|
||||
this.customerType = 'webshop';
|
||||
} else {
|
||||
this.p4mUser = false;
|
||||
const includesGuest = this.enabledOptions.some((o) => o.value === 'guest');
|
||||
this.customerType = includesGuest ? 'guest' : first(this.enabledOptions)?.value;
|
||||
}
|
||||
} else {
|
||||
// wenn customerType nicht gesetzt wird und p4mUser false ist,
|
||||
// dann customerType auf den ersten verfügbaren setzen der nicht mit dem aktuellen customerType übereinstimmt.
|
||||
this.customerType = first(this.enabledOptions.filter((o) => o.value === this.customerType))?.value ?? this.customerType;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.customerType !== initial.customerType || this.p4mUser !== initial.p4mUser) {
|
||||
this.onChange(this.value);
|
||||
this.onTouched();
|
||||
this.valueChanges.emit(this.value);
|
||||
}
|
||||
|
||||
this.checkboxes?.find((c) => c.name === this.customerType)?.writeValue(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CustomerTypeSelectorComponent } from './customer-type-selector.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CheckboxComponent } from '@shared/components/checkbox';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, FormsModule, CheckboxComponent],
|
||||
exports: [CustomerTypeSelectorComponent],
|
||||
declarations: [CustomerTypeSelectorComponent],
|
||||
})
|
||||
export class CustomerTypeSelectorModule {}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './customer-type-selector.component';
|
||||
export * from './customer-type-selector.module';
|
||||
@@ -0,0 +1,4 @@
|
||||
<ui-checkbox [formControl]="control" [tabindex]="tabIndexStart" [autofocus]="focusAfterInit" [readonly]="readonly">
|
||||
Bitte unterschreiben Sie die Teilnahmebedingungen im Kundenkartenformular, um die Prämiennutzung zu ermöglichen.
|
||||
{{ requiredMark ? '*' : '' }}
|
||||
</ui-checkbox>
|
||||
@@ -0,0 +1,11 @@
|
||||
:host {
|
||||
@apply block;
|
||||
}
|
||||
|
||||
ui-checkbox {
|
||||
@apply font-semibold;
|
||||
}
|
||||
|
||||
::ng-deep app-accept-agb-form-block ui-checkbox {
|
||||
align-items: flex-start !important;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { UntypedFormControl } from '@angular/forms';
|
||||
import { FormBlockControl } from '../form-block';
|
||||
|
||||
@Component({
|
||||
selector: 'app-accept-agb-form-block',
|
||||
templateUrl: 'accept-agb-form-block.component.html',
|
||||
styleUrls: ['accept-agb-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
exportAs: 'appAcceptAGBFormBlock',
|
||||
})
|
||||
export class AcceptAGBFormBlockComponent extends FormBlockControl<boolean> {
|
||||
get tabIndexEnd(): number {
|
||||
return this.tabIndexStart;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
initializeControl(data?: boolean): void {
|
||||
this.control = new UntypedFormControl(data ?? false, this.getValidatorFn(), this.getAsyncValidatorFn());
|
||||
}
|
||||
|
||||
_patchValue(update: { previous: boolean; current: boolean }): void {
|
||||
this.control.patchValue(update.current);
|
||||
}
|
||||
|
||||
updateValidators(): void {
|
||||
this.control.setValidators(this.getValidatorFn());
|
||||
}
|
||||
|
||||
setValue(value: boolean): void {
|
||||
this.control.setValue(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { AcceptAGBFormBlockComponent } from './accept-agb-form-block.component';
|
||||
import { UiCheckboxModule } from '@ui/checkbox';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiCommonModule, UiCheckboxModule, ReactiveFormsModule],
|
||||
exports: [AcceptAGBFormBlockComponent],
|
||||
declarations: [AcceptAGBFormBlockComponent],
|
||||
})
|
||||
export class AcceptAGBFormBlockModule {}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './accept-agb-form-block.component';
|
||||
export * from './accept-agb-form-block.module';
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface AddressFormBlockData {
|
||||
street?: string;
|
||||
streetNumber?: string;
|
||||
zipCode?: string;
|
||||
city?: string;
|
||||
info?: string;
|
||||
country?: string;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<ng-container [formGroup]="control">
|
||||
<ui-form-control label="Straße" [requiredMark]="requiredMarks.includes('street') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="street" [tabindex]="tabIndexStart" [autofocus]="focusAfterInit" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Hausnummer" [requiredMark]="requiredMarks.includes('streetNumber') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="streetNumber" [tabindex]="tabIndexStart + 1" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="PLZ" [requiredMark]="requiredMarks.includes('zipCode') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="zipCode" [tabindex]="tabIndexStart + 2" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Ort" [requiredMark]="requiredMarks.includes('city') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="city" [tabindex]="tabIndexStart + 3" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control class="col-span-2" label="Adresszusatz" [clearable]="false" [requiredMark]="requiredMarks.includes('info') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="info" [tabindex]="tabIndexStart + 4" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control class="col-span-2" label="Land" [clearable]="true" [requiredMark]="requiredMarks.includes('country') ? '*' : ''">
|
||||
<ui-select formControlName="country" [tabindex]="tabIndexStart + 5" [readonly]="readonly">
|
||||
<ui-select-option *ngFor="let country of countries || (countries$ | async)" [label]="country.name" [value]="country.isO3166_A_3">
|
||||
</ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply grid grid-cols-2 gap-x-8;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { Component, ChangeDetectionStrategy, Input, OnInit, ChangeDetectorRef } from '@angular/core';
|
||||
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { CountryDTO } from '@swagger/crm';
|
||||
import { camelCase } from 'lodash';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { FormBlockGroup } from '../form-block';
|
||||
import { AddressFormBlockData } from './address-form-block-data';
|
||||
|
||||
@Component({
|
||||
selector: 'app-address-form-block',
|
||||
templateUrl: 'address-form-block.component.html',
|
||||
styleUrls: ['address-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AddressFormBlockComponent extends FormBlockGroup<AddressFormBlockData> implements OnInit {
|
||||
@Input() countries: CountryDTO[];
|
||||
|
||||
@Input() defaults: Partial<AddressFormBlockData>;
|
||||
|
||||
get tabIndexEnd(): number {
|
||||
return this.tabIndexStart + 5;
|
||||
}
|
||||
|
||||
countries$: Observable<CountryDTO[]>;
|
||||
|
||||
constructor(private readonly _customerService: CrmCustomerService, private _cdr: ChangeDetectorRef) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
|
||||
this.countries$ = this._customerService.getCountries().pipe(map((r) => r.result));
|
||||
}
|
||||
|
||||
initializeControl(data?: AddressFormBlockData): void {
|
||||
this.control = new UntypedFormGroup({
|
||||
country: new UntypedFormControl(data?.country ?? this.defaults?.country ?? '', this.getValidatorFn('country')),
|
||||
city: new UntypedFormControl(data?.city ?? this.defaults?.city ?? '', this.getValidatorFn('city')),
|
||||
street: new UntypedFormControl(data?.street ?? this.defaults?.street ?? '', this.getValidatorFn('street')),
|
||||
streetNumber: new UntypedFormControl(data?.streetNumber ?? this.defaults?.streetNumber ?? '', this.getValidatorFn('streetNumber')),
|
||||
zipCode: new UntypedFormControl(data?.zipCode ?? this.defaults?.zipCode ?? '', this.getValidatorFn('zipCode')),
|
||||
info: new UntypedFormControl(data?.info ?? this.defaults?.info ?? '', this.getValidatorFn('info')),
|
||||
});
|
||||
}
|
||||
|
||||
_patchValue(update: { previous: AddressFormBlockData; current: AddressFormBlockData }): void {
|
||||
this.control.patchValue({
|
||||
country: update.current.country ?? '',
|
||||
city: update.current.city ?? '',
|
||||
street: update.current.street ?? '',
|
||||
streetNumber: update.current.streetNumber ?? '',
|
||||
zipCode: update.current.zipCode ?? '',
|
||||
info: update.current.info ?? '',
|
||||
});
|
||||
}
|
||||
|
||||
setAddressValidationError(invalidProperties: Record<keyof AddressFormBlockData, string>) {
|
||||
const keys = Object.keys(invalidProperties);
|
||||
for (const key of keys) {
|
||||
this.control.get(camelCase(key))?.setErrors({ validateAddress: invalidProperties[key] });
|
||||
}
|
||||
this.control.markAllAsTouched();
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
|
||||
updateValidators(): void {
|
||||
this.control.get('country')?.setValidators(this.getValidatorFn('country'));
|
||||
this.control.get('city')?.setValidators(this.getValidatorFn('city'));
|
||||
this.control.get('street')?.setValidators(this.getValidatorFn('street'));
|
||||
this.control.get('streetNumber')?.setValidators(this.getValidatorFn('streetNumber'));
|
||||
this.control.get('zipCode')?.setValidators(this.getValidatorFn('zipCode'));
|
||||
this.control.get('info')?.setValidators(this.getValidatorFn('info'));
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { AddressFormBlockComponent } from './address-form-block.component';
|
||||
import { UiFormControlModule } from '@ui/form-control';
|
||||
import { UiInputModule } from '@ui/input';
|
||||
import { UiSelectModule } from '@ui/select';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiCommonModule, UiFormControlModule, UiInputModule, UiSelectModule, ReactiveFormsModule],
|
||||
exports: [AddressFormBlockComponent],
|
||||
declarations: [AddressFormBlockComponent],
|
||||
})
|
||||
export class AddressFormBlockModule {}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './address-form-block-data';
|
||||
export * from './address-form-block.component';
|
||||
export * from './address-form-block.module';
|
||||
@@ -0,0 +1,3 @@
|
||||
<ui-form-control label="Geburtsdatum (TT.MM.JJJJ)" [requiredMark]="requiredMark ? '*' : ''">
|
||||
<input uiDateInput type="text" [formControl]="control" [tabindex]="tabIndexStart" [autofocus]="focusAfterInit" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply block;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { UntypedFormControl, Validators } from '@angular/forms';
|
||||
import { UiValidators } from '@ui/validators';
|
||||
import { FormBlockControl } from '../form-block';
|
||||
|
||||
@Component({
|
||||
selector: 'app-birth-date-form-block',
|
||||
templateUrl: 'birth-date-form-block.component.html',
|
||||
styleUrls: ['birth-date-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class BirthDateFormBlockComponent extends FormBlockControl<Date | string> {
|
||||
get tabIndexEnd() {
|
||||
return this.tabIndexStart;
|
||||
}
|
||||
|
||||
initializeControl(data?: Date): void {
|
||||
this.control = new UntypedFormControl(data, [...this.getValidatorFn(), UiValidators.date]);
|
||||
}
|
||||
|
||||
_patchValue(update: { previous: Date; current: Date }): void {
|
||||
this.control.patchValue(update.current);
|
||||
}
|
||||
|
||||
updateValidators(): void {
|
||||
this.control.setValidators([...this.getValidatorFn(), UiValidators.date]);
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { BirthDateFormBlockComponent } from './birth-date-form-block.component';
|
||||
import { UiFormControlModule } from '@ui/form-control';
|
||||
import { UiInputModule } from '@ui/input';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, ReactiveFormsModule, UiFormControlModule, UiInputModule, UiCommonModule],
|
||||
exports: [BirthDateFormBlockComponent],
|
||||
declarations: [BirthDateFormBlockComponent],
|
||||
})
|
||||
export class BirthDateFormBlockModule {}
|
||||
@@ -0,0 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './birth-date-form-block.component';
|
||||
export * from './birth-date-form-block.module';
|
||||
// end:ng42.barrel
|
||||
@@ -0,0 +1,12 @@
|
||||
import { AddressFormBlockData } from '../address';
|
||||
import { NameFormBlockData } from '../name/name-form-block-data';
|
||||
import { OrganisationFormBlockData } from '../organisation/organisation-form-block-data';
|
||||
|
||||
export interface DeviatingAddressFormBlockData {
|
||||
deviatingAddress?: boolean;
|
||||
organisation?: OrganisationFormBlockData;
|
||||
name?: NameFormBlockData;
|
||||
address?: AddressFormBlockData;
|
||||
email?: string;
|
||||
phoneNumbers?: { mobile?: string; phone?: string };
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<ui-checkbox [formControl]="deviatingAddress" [tabindex]="tabIndexStart" [autofocus]="focusAfterInit" [readonly]="readonly">
|
||||
<ng-content></ng-content>
|
||||
</ui-checkbox>
|
||||
<div class="address-block" *ngIf="control.value.deviatingAddress">
|
||||
<div class="wrapper">
|
||||
<app-organisation-form-block
|
||||
*ngIf="organisation"
|
||||
[tabIndexStart]="tabIndexStart + 1"
|
||||
#orgaBlock
|
||||
(onInit)="addOrganisationGroup($event)"
|
||||
(onDestroy)="removeOrganisationGroup()"
|
||||
[data]="data?.organisation"
|
||||
#nameFormBlock
|
||||
[tabIndexStart]="tabIndexStart + 1"
|
||||
[requiredMarks]="organisationRequiredMarks"
|
||||
[validatorFns]="organisationValidatorFns"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
</app-organisation-form-block>
|
||||
<app-name-form-block
|
||||
(onInit)="addNameGroup($event)"
|
||||
(onDestroy)="removeNameGroup()"
|
||||
[data]="data?.name"
|
||||
#nameFormBlock
|
||||
[requiredMarks]="nameRequiredMarks"
|
||||
[validatorFns]="nameValidatorFns"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
</app-name-form-block>
|
||||
<app-address-form-block
|
||||
#addressFormBlock
|
||||
(onInit)="addAddressGroup($event)"
|
||||
(onDestroy)="removeAddressGroup()"
|
||||
[data]="data?.address"
|
||||
[tabIndexStart]="nameFormBlock?.tabIndexEnd + 1"
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidatorFns"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
</app-address-form-block>
|
||||
<app-email-form-block
|
||||
*ngIf="email"
|
||||
#emailFormBlock
|
||||
(onInit)="addEmailGroup($event)"
|
||||
(onDestroy)="removeEmailGroup()"
|
||||
[data]="data?.email"
|
||||
[requiredMark]="emailRequiredMark"
|
||||
[validatorFns]="emailValidationFns"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
</app-email-form-block>
|
||||
<app-phone-numbers-form-block
|
||||
*ngIf="phoneNumbers"
|
||||
(onInit)="addPhoneNumbersGroup($event)"
|
||||
(onDestroy)="removePhoneNumbersGroup()"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
[tabIndexStart]="emailFormBlock?.tabIndexEnd+1" [requiredMarks]="phoneNumbersRequiredMarks" [validatorFns]="phoneNumbersValidatorFns">
|
||||
</app-phone-numbers-form-block>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
:host {
|
||||
@apply block;
|
||||
}
|
||||
|
||||
ui-checkbox {
|
||||
@apply font-semibold;
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
import { Component, ChangeDetectionStrategy, ViewChild, ElementRef, Self, AfterViewInit, ChangeDetectorRef, Input } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, ValidatorFn } from '@angular/forms';
|
||||
import { FormBlock, FormBlockGroup } from '../form-block';
|
||||
import { DeviatingAddressFormBlockData } from './deviating-address-form-block-data';
|
||||
import { NameFormBlockComponent } from '../name';
|
||||
import { AddressFormBlockComponent, AddressFormBlockData } from '../address';
|
||||
import { NameFormBlockData } from '../name/name-form-block-data';
|
||||
import { OrganisationFormBlockComponent } from '../organisation';
|
||||
import { OrganisationFormBlockData } from '../organisation/organisation-form-block-data';
|
||||
import { EmailFormBlockComponent } from '../email';
|
||||
import { PhoneNumbersFormBlockComponent, PhoneNumbersFormBlockData } from '../phone-numbers';
|
||||
|
||||
@Component({
|
||||
selector: 'app-deviating-address-form-block',
|
||||
templateUrl: 'deviating-address-form-block.component.html',
|
||||
styleUrls: ['deviating-address-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
exportAs: 'appDeviatingAddressFormBlock',
|
||||
})
|
||||
export class DeviatingAddressFormBlockComponent extends FormBlockGroup<DeviatingAddressFormBlockData> implements AfterViewInit {
|
||||
@Input()
|
||||
organisation = false;
|
||||
|
||||
@Input()
|
||||
email = false;
|
||||
|
||||
@Input()
|
||||
phoneNumbers = false;
|
||||
|
||||
@ViewChild(AddressFormBlockComponent, { static: false })
|
||||
private readonly _addressFormBlock: AddressFormBlockComponent;
|
||||
|
||||
@ViewChild(EmailFormBlockComponent, { static: false })
|
||||
private readonly _emailFormBlock: AddressFormBlockComponent;
|
||||
|
||||
@ViewChild(PhoneNumbersFormBlockComponent, { static: false })
|
||||
private readonly _phoneNumbersFormBlock: AddressFormBlockComponent;
|
||||
|
||||
get tabIndexEnd() {
|
||||
return this.tabIndexStart + 10;
|
||||
}
|
||||
|
||||
@Input() defaults: Partial<DeviatingAddressFormBlockData>;
|
||||
|
||||
@Input()
|
||||
nameRequiredMarks: Array<keyof NameFormBlockData>;
|
||||
|
||||
@Input()
|
||||
nameValidatorFns: Record<keyof NameFormBlockData, ValidatorFn[]>;
|
||||
|
||||
@Input()
|
||||
addressRequiredMarks: Array<keyof AddressFormBlockData>;
|
||||
|
||||
@Input()
|
||||
addressValidatorFns: Record<keyof AddressFormBlockData, ValidatorFn[]>;
|
||||
|
||||
@Input()
|
||||
organisationRequiredMarks: Array<keyof OrganisationFormBlockData>;
|
||||
|
||||
@Input()
|
||||
organisationValidatorFns: Record<keyof OrganisationFormBlockData, ValidatorFn[]>;
|
||||
|
||||
@Input()
|
||||
emailRequiredMark = false;
|
||||
|
||||
@Input()
|
||||
emailValidationFns: ValidatorFn[] = [];
|
||||
|
||||
@Input()
|
||||
phoneNumbersRequiredMarks: Array<keyof PhoneNumbersFormBlockData>;
|
||||
|
||||
@Input()
|
||||
phoneNumbersValidatorFns: Record<keyof PhoneNumbersFormBlockData, ValidatorFn[]>;
|
||||
|
||||
get deviatingAddress() {
|
||||
return this.control.get('deviatingAddress') as UntypedFormControl;
|
||||
}
|
||||
|
||||
constructor(private readonly _fb: UntypedFormBuilder, @Self() private _elementRef: ElementRef, private _cdr: ChangeDetectorRef) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {}
|
||||
|
||||
initializeControl(data?: DeviatingAddressFormBlockData): void {
|
||||
this.control = this._fb.group({
|
||||
deviatingAddress: this._fb.control(data?.deviatingAddress ?? false, this.getValidatorFn('deviatingAddress')),
|
||||
});
|
||||
}
|
||||
|
||||
_patchValue(update: { previous: DeviatingAddressFormBlockData; current: DeviatingAddressFormBlockData }): void {
|
||||
this.control.patchValue({
|
||||
deviatingAddress: update.current?.deviatingAddress ?? false,
|
||||
});
|
||||
}
|
||||
|
||||
addOrganisationGroup(cmp: FormBlock<OrganisationFormBlockData, UntypedFormGroup>) {
|
||||
if (!this.control.contains('organisation')) {
|
||||
this.control.addControl('organisation', cmp.control);
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
removeOrganisationGroup() {
|
||||
if (this.control.contains('organisation')) {
|
||||
this.control.removeControl('organisation');
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
addNameGroup(cmp: FormBlock<NameFormBlockData, UntypedFormGroup>) {
|
||||
if (!this.control.contains('name')) {
|
||||
this.control.addControl('name', cmp.control);
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
removeNameGroup() {
|
||||
if (this.control.contains('name')) {
|
||||
this.control.removeControl('name');
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
addAddressGroup(cmp: FormBlock<AddressFormBlockData, UntypedFormGroup>) {
|
||||
if (!this.control.contains('address')) {
|
||||
this.control.addControl('address', cmp.control);
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
removeAddressGroup() {
|
||||
if (this.control.contains('address')) {
|
||||
this.control.removeControl('address');
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
addEmailGroup(cmp: FormBlock<string, UntypedFormControl>) {
|
||||
if (!this.control.contains('email')) {
|
||||
this.control.addControl('email', cmp.control);
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
removeEmailGroup() {
|
||||
if (this.control.contains('email')) {
|
||||
this.control.removeControl('email');
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
addPhoneNumbersGroup(cmp: FormBlock<PhoneNumbersFormBlockData, UntypedFormGroup>) {
|
||||
if (!this.control.contains('phoneNumbers')) {
|
||||
this.control.addControl('phoneNumbers', cmp.control);
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
removePhoneNumbersGroup() {
|
||||
if (this.control.contains('phoneNumbers')) {
|
||||
this.control.removeControl('phoneNumbers');
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
setAddressValidationError(invalidProperties: Record<keyof AddressFormBlockData, string>) {
|
||||
this._addressFormBlock?.setAddressValidationError(invalidProperties);
|
||||
}
|
||||
|
||||
updateValidators(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { DeviatingAddressFormBlockComponent } from './deviating-address-form-block.component';
|
||||
import { UiCheckboxModule } from '@ui/checkbox';
|
||||
import { AddressFormBlockModule } from '../address';
|
||||
import { NameFormBlockModule } from '../name';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { OrganisationFormBlockModule } from '../organisation';
|
||||
import { EmailFormBlockModule } from '../email';
|
||||
import { PhoneNumbersFormBlockModule } from '../phone-numbers';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
UiCheckboxModule,
|
||||
NameFormBlockModule,
|
||||
AddressFormBlockModule,
|
||||
OrganisationFormBlockModule,
|
||||
EmailFormBlockModule,
|
||||
PhoneNumbersFormBlockModule,
|
||||
UiCommonModule,
|
||||
],
|
||||
exports: [DeviatingAddressFormBlockComponent],
|
||||
declarations: [DeviatingAddressFormBlockComponent],
|
||||
})
|
||||
export class DeviatingAddressFormBlockComponentModule {}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './deviating-address-form-block-data';
|
||||
export * from './deviating-address-form-block.component';
|
||||
export * from './deviating-address-form-block.module';
|
||||
@@ -0,0 +1,3 @@
|
||||
<ui-form-control label="E-Mail" [requiredMark]="requiredMark ? '*' : ''">
|
||||
<input uiInput type="mail" [formControl]="control" [tabindex]="tabIndexStart" [autofocus]="focusAfterInit" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { UntypedFormControl } from '@angular/forms';
|
||||
import { FormBlockControl } from '../form-block';
|
||||
import { validateEmail } from '../../../validators/email-validator';
|
||||
|
||||
@Component({
|
||||
selector: 'app-email-form-block',
|
||||
templateUrl: 'email-form-block.component.html',
|
||||
styleUrls: ['email-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class EmailFormBlockComponent extends FormBlockControl<string> {
|
||||
get tabIndexEnd() {
|
||||
return this.tabIndexStart;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
updateValidators(): void {
|
||||
this.control.setValidators([...this.getValidatorFn(), validateEmail]);
|
||||
this.control.setAsyncValidators(this.getAsyncValidatorFn());
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
|
||||
initializeControl(data?: string): void {
|
||||
this.control = new UntypedFormControl(data, [...this.getValidatorFn(), validateEmail], this.getAsyncValidatorFn());
|
||||
}
|
||||
|
||||
_patchValue(update: { previous: string; current: string }): void {
|
||||
this.control.patchValue(update.current);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { EmailFormBlockComponent } from './email-form-block.component';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiFormControlModule } from '@ui/form-control';
|
||||
import { UiInputModule } from '@ui/input';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiCommonModule, ReactiveFormsModule, UiFormControlModule, UiInputModule],
|
||||
exports: [EmailFormBlockComponent],
|
||||
declarations: [EmailFormBlockComponent],
|
||||
})
|
||||
export class EmailFormBlockModule {}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './email-form-block.component';
|
||||
export * from './email-form-block.module';
|
||||
@@ -0,0 +1,179 @@
|
||||
import { BooleanInput, coerceBooleanProperty, NumberInput, coerceNumberProperty } from '@angular/cdk/coercion';
|
||||
import { Directive, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
||||
import { AbstractControl, AsyncValidatorFn, UntypedFormControl, UntypedFormGroup, ValidatorFn } from '@angular/forms';
|
||||
import { isEqual } from 'lodash';
|
||||
import { Subject } from 'rxjs';
|
||||
import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
|
||||
|
||||
@Directive()
|
||||
export abstract class FormBlock<TD, TC extends AbstractControl> implements OnInit, OnDestroy {
|
||||
private _readonly = false;
|
||||
|
||||
@Input()
|
||||
get readonly(): boolean {
|
||||
return this._readonly;
|
||||
}
|
||||
set readonly(value: BooleanInput) {
|
||||
this._readonly = coerceBooleanProperty(value);
|
||||
}
|
||||
|
||||
private _focusAfterInit = false;
|
||||
@Input()
|
||||
get focusAfterInit(): boolean {
|
||||
return this._focusAfterInit;
|
||||
}
|
||||
set focusAfterInit(value: BooleanInput) {
|
||||
this._focusAfterInit = coerceBooleanProperty(value);
|
||||
}
|
||||
|
||||
readonly onDestroy$ = new Subject<void>();
|
||||
|
||||
private _tabIndexStart: number;
|
||||
|
||||
@Input()
|
||||
get tabIndexStart(): number {
|
||||
return this._tabIndexStart;
|
||||
}
|
||||
set tabIndexStart(value: NumberInput) {
|
||||
this._tabIndexStart = coerceNumberProperty(value);
|
||||
}
|
||||
|
||||
abstract tabIndexEnd: NumberInput;
|
||||
|
||||
private _data: TD;
|
||||
|
||||
@Input()
|
||||
get data(): TD | undefined {
|
||||
return this._data;
|
||||
}
|
||||
set data(value: TD) {
|
||||
const previous = this._data;
|
||||
this._data = value;
|
||||
|
||||
if (this.control && !isEqual(previous, value)) {
|
||||
this._patchValue({
|
||||
previous,
|
||||
current: value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Output()
|
||||
dataChanges = new EventEmitter<TD>();
|
||||
|
||||
@Output()
|
||||
onInit = new EventEmitter<FormBlock<TD, TC>>();
|
||||
|
||||
@Output()
|
||||
onDestroy = new EventEmitter<void>();
|
||||
|
||||
control: TC;
|
||||
|
||||
get value(): TD {
|
||||
return this.control.value;
|
||||
}
|
||||
|
||||
get valid(): boolean {
|
||||
return this.control.valid;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initializeControl(this.data);
|
||||
this.control.valueChanges
|
||||
.pipe(takeUntil(this.onDestroy$), distinctUntilChanged(isEqual))
|
||||
.subscribe((value) => this.dataChanges.emit(value));
|
||||
this.onInit.emit(this);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy$.next();
|
||||
this.onDestroy$.complete();
|
||||
this.onDestroy.emit();
|
||||
}
|
||||
|
||||
abstract initializeControl(data?: TD): void;
|
||||
|
||||
abstract _patchValue(update: { previous: TD; current: TD }): void;
|
||||
}
|
||||
|
||||
@Directive()
|
||||
export abstract class FormBlockControl<TD> extends FormBlock<TD, UntypedFormControl> {
|
||||
@Input()
|
||||
requiredMark: boolean;
|
||||
|
||||
private _validatorFns: ValidatorFn[];
|
||||
|
||||
@Input()
|
||||
get validatorFns() {
|
||||
return this._validatorFns;
|
||||
}
|
||||
set validatorFns(value: ValidatorFn[]) {
|
||||
this._validatorFns = value;
|
||||
if (this.control) {
|
||||
this.updateValidators();
|
||||
}
|
||||
}
|
||||
|
||||
private _asyncValidatorFns: AsyncValidatorFn[];
|
||||
|
||||
@Input()
|
||||
get asyncValidatorFns() {
|
||||
return this._asyncValidatorFns;
|
||||
}
|
||||
set asyncValidatorFns(value: AsyncValidatorFn[]) {
|
||||
this._asyncValidatorFns = value;
|
||||
if (this.control) {
|
||||
this.updateValidators();
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
abstract updateValidators(): void;
|
||||
|
||||
getValidatorFn(): ValidatorFn[] {
|
||||
return this.validatorFns ?? [];
|
||||
}
|
||||
|
||||
getAsyncValidatorFn(): AsyncValidatorFn[] {
|
||||
return this.asyncValidatorFns ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
@Directive()
|
||||
export abstract class FormBlockGroup<TD> extends FormBlock<TD, UntypedFormGroup> {
|
||||
private _requiredMarks: Array<keyof TD>;
|
||||
|
||||
@Input()
|
||||
get requiredMarks() {
|
||||
return this._requiredMarks ?? [];
|
||||
}
|
||||
set requiredMarks(value: Array<keyof TD>) {
|
||||
this._requiredMarks = value;
|
||||
}
|
||||
|
||||
private _validatorFns: Record<keyof TD, ValidatorFn[]>;
|
||||
|
||||
@Input()
|
||||
get validatorFns() {
|
||||
return this._validatorFns ?? ({} as Record<keyof TD, ValidatorFn[]>);
|
||||
}
|
||||
set validatorFns(value: Record<keyof TD, ValidatorFn[]>) {
|
||||
this._validatorFns = value;
|
||||
if (this.control) {
|
||||
this.updateValidators();
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
abstract updateValidators(): void;
|
||||
|
||||
getValidatorFn(key: keyof TD): ValidatorFn[] {
|
||||
return this.validatorFns[key] ?? [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export * from './accept-agb';
|
||||
export * from './address';
|
||||
export * from './birth-date';
|
||||
export * from './deviating-address';
|
||||
export * from './email';
|
||||
export * from './interests';
|
||||
export * from './name';
|
||||
export * from './newsletter';
|
||||
export * from './organisation';
|
||||
export * from './p4m-number';
|
||||
export * from './phone-numbers';
|
||||
export * from './form-block';
|
||||
@@ -0,0 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './interests-form-block.component';
|
||||
export * from './interests-form-block.module';
|
||||
// end:ng42.barrel
|
||||
@@ -0,0 +1 @@
|
||||
export type InterestsFormBlockData = Record<string, boolean>;
|
||||
@@ -0,0 +1,12 @@
|
||||
<div class="interests-description">Geben Sie Interessen an, um Ihre persönlichen Kontoangaben zu verfeinern.</div>
|
||||
<div class="interests-wrapper" [formGroup]="control">
|
||||
<ui-checkbox
|
||||
*ngFor="let pair of interests | keyvalue; let idx = index"
|
||||
[formControlName]="pair.key"
|
||||
[tabindex]="tabIndexStart + idx"
|
||||
[autofocus]="focusAfterInit"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
{{ pair.value }}
|
||||
</ui-checkbox>
|
||||
</div>
|
||||
@@ -0,0 +1,11 @@
|
||||
:host {
|
||||
@apply grid grid-flow-row gap-6;
|
||||
}
|
||||
|
||||
.interests-description {
|
||||
@apply font-bold;
|
||||
}
|
||||
|
||||
.interests-wrapper {
|
||||
@apply grid grid-cols-2 gap-4 font-semibold;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { FormBlock } from '../form-block';
|
||||
import { InterestsFormBlockData } from './interests-form-block-data';
|
||||
import { LoyaltyCardService } from '@swagger/crm';
|
||||
import { shareReplay } from 'rxjs/operators';
|
||||
import { isEqual } from 'lodash';
|
||||
import { memorize } from '@utils/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-interests-form-block',
|
||||
templateUrl: 'interests-form-block.component.html',
|
||||
styleUrls: ['interests-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class InterestsFormBlockComponent extends FormBlock<InterestsFormBlockData, UntypedFormGroup> {
|
||||
private _interests: Map<string, string>;
|
||||
|
||||
get interests(): Map<string, string> {
|
||||
return this._interests;
|
||||
}
|
||||
set interests(value: Map<string, string>) {
|
||||
if (!isEqual(this._interests, value)) {
|
||||
this._interests = value;
|
||||
if (this.control) {
|
||||
this.updateInterestControls();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get tabIndexEnd() {
|
||||
return this.tabIndexStart + this.interests?.keys.length;
|
||||
}
|
||||
|
||||
constructor(private _fb: UntypedFormBuilder, private _LoyaltyCardService: LoyaltyCardService) {
|
||||
super();
|
||||
|
||||
this.getInterests().subscribe({
|
||||
next: (response) => {
|
||||
const interests = new Map<string, string>();
|
||||
response.result.forEach((preference) => {
|
||||
interests.set(preference.key, preference.value);
|
||||
});
|
||||
this.interests = interests;
|
||||
},
|
||||
error: (error) => {
|
||||
console.error(error);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@memorize({ ttl: 28800000 })
|
||||
getInterests() {
|
||||
return this._LoyaltyCardService.LoyaltyCardListInteressen().pipe(shareReplay(1));
|
||||
}
|
||||
|
||||
updateInterestControls() {
|
||||
const fData = this.data ?? {};
|
||||
this.interests?.forEach((value, key) => {
|
||||
if (!this.control.contains(key)) {
|
||||
this.control.addControl(key, new UntypedFormControl(fData[key] ?? false));
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(this.control.controls).forEach((key) => {
|
||||
if (!this.interests.has(key)) {
|
||||
this.control.removeControl(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initializeControl(data?: InterestsFormBlockData): void {
|
||||
const fData = data ?? {};
|
||||
this.control = this._fb.group({});
|
||||
|
||||
this.interests?.forEach((value, key) => {
|
||||
this.control.addControl(key, new UntypedFormControl(fData[key] ?? false));
|
||||
});
|
||||
}
|
||||
|
||||
_patchValue(update: { previous: InterestsFormBlockData; current: InterestsFormBlockData }): void {
|
||||
const fData = update.current ?? {};
|
||||
|
||||
this.interests?.forEach((value, key) => {
|
||||
this.control.get(key).patchValue(fData[key] ?? false);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { InterestsFormBlockComponent } from './interests-form-block.component';
|
||||
import { UiCheckboxModule } from '@ui/checkbox';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiCommonModule, ReactiveFormsModule, UiCheckboxModule],
|
||||
exports: [InterestsFormBlockComponent],
|
||||
declarations: [InterestsFormBlockComponent],
|
||||
})
|
||||
export class InterestsFormBlockModule {}
|
||||
@@ -0,0 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './name-form-block.component';
|
||||
export * from './name-form-block.module';
|
||||
// end:ng42.barrel
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Gender } from '@swagger/crm';
|
||||
|
||||
export interface NameFormBlockData {
|
||||
gender?: Gender;
|
||||
title?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<ng-container [formGroup]="control">
|
||||
<label class="grid grid-flow-row gap-1">
|
||||
<span>Anrede</span>
|
||||
|
||||
<shared-select formControlName="gender" placeholder="Anrede" [readonly]="readonly" [tabindex]="tabIndexStart">
|
||||
<shared-select-option [value]="2">Herr</shared-select-option>
|
||||
<shared-select-option [value]="4">Frau</shared-select-option>
|
||||
</shared-select>
|
||||
</label>
|
||||
|
||||
<label class="grid grid-flow-row gap-1">
|
||||
<span>Titel</span>
|
||||
|
||||
<shared-select formControlName="title" placeholder="Titel" [readonly]="readonly" [tabindex]="tabIndexStart + 1">
|
||||
<shared-select-option value="Dipl.-Ing.">Dipl.-Ing.</shared-select-option>
|
||||
<shared-select-option value="Dr.">Dr.</shared-select-option>
|
||||
<shared-select-option value="Dr. med.">Dr. med.</shared-select-option>
|
||||
<shared-select-option value="Prof.">Prof.</shared-select-option>
|
||||
<shared-select-option value="Prof. Dr.">Prof. Dr.</shared-select-option>
|
||||
<shared-select-option value="RA">RA</shared-select-option>
|
||||
</shared-select>
|
||||
</label>
|
||||
|
||||
<!-- <ui-form-control [clearable]="!readonly" label="Anrede" [requiredMark]="requiredMarks.includes('gender') ? '*' : ''">
|
||||
<ui-select formControlName="gender" [tabindex]="tabIndexStart" [autofocus]="focusAfterInit" [readonly]="readonly">
|
||||
<ui-select-option [value]="2" label="Herr"></ui-select-option>
|
||||
<ui-select-option [value]="4" label="Frau"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control> -->
|
||||
<!-- <ui-form-control [clearable]="!readonly" label="Titel" [requiredMark]="requiredMarks.includes('title') ? '*' : ''">
|
||||
<ui-select formControlName="title" [tabindex]="tabIndexStart + 1" [readonly]="readonly">
|
||||
<ui-select-option value="Dipl.-Ing." label="Dipl.-Ing."></ui-select-option>
|
||||
<ui-select-option value="Dr." label="Dr."></ui-select-option>
|
||||
<ui-select-option value="Dr. med." label="Dr. med."></ui-select-option>
|
||||
<ui-select-option value="Prof." label="Prof."></ui-select-option>
|
||||
<ui-select-option value="Prof. Dr." label="Prof. Dr."></ui-select-option>
|
||||
<ui-select-option value="RA" label="RA"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control> -->
|
||||
|
||||
<ui-form-control label="Nachname" [requiredMark]="requiredMarks.includes('firstName') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="lastName" [tabindex]="tabIndexStart + 2" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Vorname" [requiredMark]="requiredMarks.includes('lastName') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="firstName" [tabindex]="tabIndexStart + 3" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply grid grid-cols-2 gap-x-8;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { FormBlockGroup } from '../form-block';
|
||||
import { NameFormBlockData } from './name-form-block-data';
|
||||
import { Gender } from '@swagger/crm';
|
||||
|
||||
@Component({
|
||||
selector: 'app-name-form-block',
|
||||
templateUrl: 'name-form-block.component.html',
|
||||
styleUrls: ['name-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class NameFormBlockComponent extends FormBlockGroup<NameFormBlockData> {
|
||||
get tabIndexEnd() {
|
||||
return this.tabIndexStart + 3;
|
||||
}
|
||||
|
||||
displayGenderNameFn = (gender: Gender) => {
|
||||
if (gender == 2) {
|
||||
return 'Herr';
|
||||
}
|
||||
if (gender == 4) {
|
||||
return 'Frau';
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
initializeControl(data?: NameFormBlockData): void {
|
||||
this.control = new UntypedFormGroup({
|
||||
gender: new UntypedFormControl(data?.gender, this.getValidatorFn('gender')),
|
||||
title: new UntypedFormControl(data?.title, this.getValidatorFn('title')),
|
||||
firstName: new UntypedFormControl(data?.firstName ?? '', this.getValidatorFn('firstName')),
|
||||
lastName: new UntypedFormControl(data?.lastName ?? '', this.getValidatorFn('lastName')),
|
||||
});
|
||||
}
|
||||
_patchValue(update: { previous: NameFormBlockData; current: NameFormBlockData }): void {
|
||||
this.control.patchValue({
|
||||
gender: update.current?.gender ?? 0,
|
||||
title: update.current?.title ?? '',
|
||||
firstName: update.current?.firstName ?? '',
|
||||
lastName: update.current?.lastName ?? '',
|
||||
});
|
||||
}
|
||||
|
||||
updateValidators(): void {
|
||||
this.control.get('gender').setValidators(this.getValidatorFn('gender'));
|
||||
this.control.get('title').setValidators(this.getValidatorFn('title'));
|
||||
this.control.get('firstName').setValidators(this.getValidatorFn('firstName'));
|
||||
this.control.get('lastName').setValidators(this.getValidatorFn('lastName'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { NameFormBlockComponent } from './name-form-block.component';
|
||||
import { UiFormControlModule } from '@ui/form-control';
|
||||
import { UiInputModule } from '@ui/input';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
import { SelectModule } from '@shared/components/select';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiCommonModule, ReactiveFormsModule, UiFormControlModule, UiInputModule, SelectModule],
|
||||
exports: [NameFormBlockComponent],
|
||||
declarations: [NameFormBlockComponent],
|
||||
})
|
||||
export class NameFormBlockModule {}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './newsletter-form-block.component';
|
||||
export * from './newsletter-form-block.module';
|
||||
@@ -0,0 +1,3 @@
|
||||
<ui-checkbox [formControl]="control" [tabindex]="tabIndexStart" [autofocus]="focusAfterInit" [readonly]="readonly">
|
||||
Erhalten Sie 250 Lesepunkte mit der Anmeldung zum Newsletter und eine Geburtstagsüberraschung
|
||||
</ui-checkbox>
|
||||
@@ -0,0 +1,11 @@
|
||||
:host {
|
||||
@apply block;
|
||||
}
|
||||
|
||||
ui-checkbox {
|
||||
@apply font-semibold;
|
||||
}
|
||||
|
||||
::ng-deep app-newsletter-form-block ui-checkbox {
|
||||
align-items: flex-start !important;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { UntypedFormControl } from '@angular/forms';
|
||||
import { FormBlock } from '../form-block';
|
||||
|
||||
@Component({
|
||||
selector: 'app-newsletter-form-block',
|
||||
templateUrl: 'newsletter-form-block.component.html',
|
||||
styleUrls: ['newsletter-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class NewsletterFormBlockComponent extends FormBlock<boolean, UntypedFormControl> {
|
||||
get tabIndexEnd() {
|
||||
return this.tabIndexStart;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
initializeControl(data?: boolean): void {
|
||||
this.control = new UntypedFormControl(data ?? false);
|
||||
}
|
||||
|
||||
_patchValue(update: { previous: boolean; current: boolean }): void {
|
||||
this.control.patchValue(update.current);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { NewsletterFormBlockComponent } from './newsletter-form-block.component';
|
||||
import { UiCheckboxModule } from '@ui/checkbox';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiCommonModule, ReactiveFormsModule, UiCheckboxModule],
|
||||
exports: [NewsletterFormBlockComponent],
|
||||
declarations: [NewsletterFormBlockComponent],
|
||||
})
|
||||
export class NewsletterFormBlockModule {}
|
||||
@@ -0,0 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './organisation-form-block.component';
|
||||
export * from './organisation-form-block.module';
|
||||
// end:ng42.barrel
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface OrganisationFormBlockData {
|
||||
name?: string;
|
||||
department?: string;
|
||||
vatId?: string;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<ng-container [formGroup]="control">
|
||||
<ui-form-control class="col-span-2" label="Firmenname" [requiredMark]="requiredMarks.includes('name') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="name" [tabindex]="tabIndexStart" [autofocus]="focusAfterInit" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
<ng-container *ngIf="appearence === 'default'">
|
||||
<ui-form-control label="Abteilung" [requiredMark]="requiredMarks.includes('department') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="department" [tabindex]="tabIndexStart + 1" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="USt-ID" [requiredMark]="requiredMarks.includes('vatId') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="vatId" [tabindex]="tabIndexStart + 2" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply grid grid-cols-2 gap-x-8;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { FormBlockGroup } from '../form-block';
|
||||
import { OrganisationFormBlockData } from './organisation-form-block-data';
|
||||
|
||||
@Component({
|
||||
selector: 'app-organisation-form-block',
|
||||
templateUrl: 'organisation-form-block.component.html',
|
||||
styleUrls: ['organisation-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class OrganisationFormBlockComponent extends FormBlockGroup<OrganisationFormBlockData> {
|
||||
@Input()
|
||||
appearence: 'default' | 'compact' = 'default';
|
||||
|
||||
get tabIndexEnd() {
|
||||
return this.tabIndexStart + 2;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
initializeControl(data?: OrganisationFormBlockData): void {
|
||||
this.control = new UntypedFormGroup({
|
||||
name: new UntypedFormControl(data?.name ?? '', this.getValidatorFn('name')),
|
||||
department: new UntypedFormControl(data?.department ?? '', this.getValidatorFn('department')),
|
||||
vatId: new UntypedFormControl(data?.vatId ?? '', this.getValidatorFn('vatId')),
|
||||
});
|
||||
}
|
||||
_patchValue(update: { previous: OrganisationFormBlockData; current: OrganisationFormBlockData }): void {
|
||||
this.control.patchValue({
|
||||
name: update.current?.name ?? '',
|
||||
department: update.current?.department ?? '',
|
||||
vatId: update.current?.vatId ?? '',
|
||||
});
|
||||
}
|
||||
|
||||
updateValidators(): void {
|
||||
this.control.setValidators(this.getValidatorFn('name'));
|
||||
this.control.get('name').updateValueAndValidity();
|
||||
this.control.setValidators(this.getValidatorFn('department'));
|
||||
this.control.get('department').updateValueAndValidity();
|
||||
this.control.setValidators(this.getValidatorFn('vatId'));
|
||||
this.control.get('vatId').updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { OrganisationFormBlockComponent } from './organisation-form-block.component';
|
||||
import { UiFormControlModule } from '@ui/form-control';
|
||||
import { UiInputModule } from '@ui/input';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiCommonModule, ReactiveFormsModule, UiFormControlModule, UiInputModule],
|
||||
exports: [OrganisationFormBlockComponent],
|
||||
declarations: [OrganisationFormBlockComponent],
|
||||
})
|
||||
export class OrganisationFormBlockModule {}
|
||||
@@ -0,0 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './p4m-number-form-block.component';
|
||||
export * from './p4m-number-form-block.module';
|
||||
// end:ng42.barrel
|
||||
@@ -0,0 +1,6 @@
|
||||
<ui-form-control label="Kundenkartencode" requiredMark="*" class="flex-grow">
|
||||
<input uiInput type="text" [formControl]="control" [tabindex]="tabIndexStart" [readonly]="readonly" [autofocus]="focusAfterInit" />
|
||||
</ui-form-control>
|
||||
<button type="button" *ngIf="!readonly && canScan()" (click)="scan()">
|
||||
<shared-icon icon="barcode-scan" [size]="32"></shared-icon>
|
||||
</button>
|
||||
@@ -0,0 +1,11 @@
|
||||
:host {
|
||||
@apply block relative;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply absolute -right-2 top-0 h-14 w-14 border-none outline-none bg-transparent items-center justify-center rounded-full bg-brand;
|
||||
|
||||
shared-icon {
|
||||
@apply flex justify-center items-center text-white;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||
import { UntypedFormControl, Validators } from '@angular/forms';
|
||||
import { FormBlockControl } from '../form-block';
|
||||
import { ScanAdapterService } from '@adapter/scan';
|
||||
|
||||
@Component({
|
||||
selector: 'app-p4m-number-form-block',
|
||||
templateUrl: 'p4m-number-form-block.component.html',
|
||||
styleUrls: ['p4m-number-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class P4mNumberFormBlockComponent extends FormBlockControl<string> {
|
||||
get tabIndexEnd() {
|
||||
return this.tabIndexStart;
|
||||
}
|
||||
|
||||
constructor(private scanAdapter: ScanAdapterService, private changeDetectorRef: ChangeDetectorRef) {
|
||||
super();
|
||||
}
|
||||
|
||||
updateValidators(): void {
|
||||
this.control.setValidators([...this.getValidatorFn()]);
|
||||
this.control.setAsyncValidators(this.getAsyncValidatorFn());
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
|
||||
initializeControl(data?: string): void {
|
||||
this.control = new UntypedFormControl(data ?? '', [Validators.required], this.getAsyncValidatorFn());
|
||||
}
|
||||
|
||||
_patchValue(update: { previous: string; current: string }): void {
|
||||
this.control.patchValue(update.current);
|
||||
}
|
||||
|
||||
scan() {
|
||||
this.scanAdapter.scan().subscribe((result) => {
|
||||
this.control.patchValue(result);
|
||||
this.changeDetectorRef.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
canScan() {
|
||||
return this.scanAdapter.isReady();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { P4mNumberFormBlockComponent } from './p4m-number-form-block.component';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiFormControlModule } from '@ui/form-control';
|
||||
import { UiInputModule } from '@ui/input';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiCommonModule, ReactiveFormsModule, UiFormControlModule, UiInputModule, IconComponent],
|
||||
exports: [P4mNumberFormBlockComponent],
|
||||
declarations: [P4mNumberFormBlockComponent],
|
||||
})
|
||||
export class P4mNumberFormBlockModule {}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './phone-numbers-form-block-data';
|
||||
export * from './phone-numbers-form-block.component';
|
||||
export * from './phone-numbers-form-block.module';
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface PhoneNumbersFormBlockData {
|
||||
phone: string;
|
||||
mobile: string;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<ng-container [formGroup]="control">
|
||||
<ui-form-control label="Festnetznummer" [requiredMark]="requiredMarks.includes('phone') ? '*' : ''">
|
||||
<input uiInput type="tel" formControlName="phone" [tabindex]="tabIndexStart" [autofocus]="focusAfterInit" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Mobilnummer" [clearable]="false" [requiredMark]="requiredMarks.includes('mobile') ? '*' : ''">
|
||||
<input uiInput type="tel" formControlName="mobile" [tabindex]="tabIndexStart + 1" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user