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) {
|
private updateProcessCount(processId: number, count: number) {
|
||||||
this.applicationService.patchProcessData(processId, { count });
|
this.applicationService.patchProcessData(processId, { count });
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,15 @@ import {
|
|||||||
NotificationChannel,
|
NotificationChannel,
|
||||||
PayerDTO,
|
PayerDTO,
|
||||||
PayerService,
|
PayerService,
|
||||||
|
QueryTokenDTO,
|
||||||
ResponseArgsOfIEnumerableOfBonusCardInfoDTO,
|
ResponseArgsOfIEnumerableOfBonusCardInfoDTO,
|
||||||
ShippingAddressDTO,
|
ShippingAddressDTO,
|
||||||
ShippingAddressService,
|
ShippingAddressService,
|
||||||
} from '@swagger/crm';
|
} 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 { PagedResult, Result } from 'apps/domain/defs/src/public-api';
|
||||||
import { Observable, of, ReplaySubject } from 'rxjs';
|
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' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class CrmCustomerService {
|
export class CrmCustomerService {
|
||||||
@@ -38,6 +39,14 @@ export class CrmCustomerService {
|
|||||||
private loyaltyCardService: LoyaltyCardService
|
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[]>> {
|
complete(queryString: string, filter?: { [key: string]: string }): Observable<Result<AutocompleteDTO[]>> {
|
||||||
return this.customerService.CustomerCustomerAutocomplete({
|
return this.customerService.CustomerCustomerAutocomplete({
|
||||||
input: queryString,
|
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>> {
|
getCustomersByCustomerCardNumber(queryString: string): Observable<PagedResult<CustomerInfoDTO>> {
|
||||||
return this.customerService.CustomerGetCustomerByBonuscard(!!queryString ? queryString : undefined);
|
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 { ScanAdapterModule, ScanAdapterService, ScanditScanAdapterModule } from '@adapter/scan';
|
||||||
import { RootStateService } from './store/root-state.service';
|
import { RootStateService } from './store/root-state.service';
|
||||||
import * as Commands from './commands';
|
import * as Commands from './commands';
|
||||||
import { UiIconModule, UI_ICON_CFG } from '@ui/icon';
|
|
||||||
import { PreviewComponent } from './preview';
|
import { PreviewComponent } from './preview';
|
||||||
import { NativeContainerService } from 'native-container';
|
import { NativeContainerService } from 'native-container';
|
||||||
import { ShellModule } from '@shared/shell';
|
import { ShellModule } from '@shared/shell';
|
||||||
import { MainComponent } from './main.component';
|
import { MainComponent } from './main.component';
|
||||||
|
import { IconModule } from '@shared/components/icon';
|
||||||
|
|
||||||
registerLocaleData(localeDe, localeDeExtra);
|
registerLocaleData(localeDe, localeDeExtra);
|
||||||
registerLocaleData(localeDe, 'de', localeDeExtra);
|
registerLocaleData(localeDe, 'de', localeDeExtra);
|
||||||
@@ -106,7 +106,7 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
|
|||||||
ScanAdapterModule.forRoot(),
|
ScanAdapterModule.forRoot(),
|
||||||
ScanditScanAdapterModule.forRoot(),
|
ScanditScanAdapterModule.forRoot(),
|
||||||
PlatformModule,
|
PlatformModule,
|
||||||
UiIconModule.forRoot(),
|
IconModule.forRoot(),
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
@@ -135,11 +135,6 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
|
|||||||
useClass: IsaErrorHandler,
|
useClass: IsaErrorHandler,
|
||||||
},
|
},
|
||||||
{ provide: LOCALE_ID, useValue: 'de-DE' },
|
{ provide: LOCALE_ID, useValue: 'de-DE' },
|
||||||
{
|
|
||||||
provide: UI_ICON_CFG,
|
|
||||||
useFactory: (config: Config) => config.get('@ui/icon'),
|
|
||||||
deps: [Config],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
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,
|
"checkForUpdates": 3600000,
|
||||||
"licence": {
|
"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=="
|
"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,
|
"checkForUpdates": 3600000,
|
||||||
"licence": {
|
"licence": {
|
||||||
"scandit": "AfHi/mY+RbwJD5nC7SuWn3I14pFUOfSbQ2QG//4aV3zWQjwix30kHqsqraA8ZiipDBql8YlwIyV6VPBMUiAX4s9YHDxHHsWwq2BUB3ImzDEcU1jmMH/5yakGUYpCQ68D0iZ8SG9sS0QBb3iFdCHc1r9DFr1cMTxM7zOvb/AUoIVmieHZXnx9ioUgCvczsLiuX3hwvTW3lhbvJ4uUyqTWK4sWFVwoY4AIWSFrPwwrkV2DksMKT5fMJT3GWgPypvTIGwWvpRfLWwKlc1Z3ckyb84khsnaWD2wr+hdgu/K8YIMmgGszm5KIZ/G05YfDNZtQ4jby+5RZvQwWR8rxM35rJgf73OkMSpuL9jw3T0TTAlvpkGRLzVVuCw9VjlBLqfPNEZ6VsEwFuAla9IYUvFHCsjypg2J6UpxHXrTYmbsSu5Jm8frVfS5znPPTO9D/4rF6ZVv2PxY9PgUgJUvwMa/VMc/nse3RRRf8RGT4rUItfJDFO8pujD76vVEWq/KixQRoMdLgDLyxhsFVftkxqhZhyEfFZzsEy49LSojJ28vpHpBWLeCQBmnZ7JZ4C5yOQiqSQV/assBq2zJN2q+vCDp8qy5j1rED1SX5Ec7JpgpgnU4chLIf5Zn7bP/hNGT3pEYBuXeDXXN8ke1pcc3fc3m0FysDG0o56XVCUqImZ8Ezi8eujZciKDrWbtljhKTj7cnfuJx0sVHF6Bh5i4YfgA/Z+NL+MtH2EVIF67e6hEz6PWYTcoh3ybBaJfxb2FNvGJutNKg04GwMhYq6K2IddBt0fDiBt0SGM0oSBlUP3DKCUmXcf2a6ASbrcqv6Wz1jHt0pY4U8bEpg7qSbW3VDyvdPgyQ="
|
"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,
|
"checkForUpdates": 3600000,
|
||||||
"licence": {
|
"licence": {
|
||||||
"scandit": "AfHi/mY+RbwJD5nC7SuWn3I14pFUOfSbQ2QG//4aV3zWQjwix30kHqsqraA8ZiipDBql8YlwIyV6VPBMUiAX4s9YHDxHHsWwq2BUB3ImzDEcU1jmMH/5yakGUYpCQ68D0iZ8SG9sS0QBb3iFdCHc1r9DFr1cMTxM7zOvb/AUoIVmieHZXnx9ioUgCvczsLiuX3hwvTW3lhbvJ4uUyqTWK4sWFVwoY4AIWSFrPwwrkV2DksMKT5fMJT3GWgPypvTIGwWvpRfLWwKlc1Z3ckyb84khsnaWD2wr+hdgu/K8YIMmgGszm5KIZ/G05YfDNZtQ4jby+5RZvQwWR8rxM35rJgf73OkMSpuL9jw3T0TTAlvpkGRLzVVuCw9VjlBLqfPNEZ6VsEwFuAla9IYUvFHCsjypg2J6UpxHXrTYmbsSu5Jm8frVfS5znPPTO9D/4rF6ZVv2PxY9PgUgJUvwMa/VMc/nse3RRRf8RGT4rUItfJDFO8pujD76vVEWq/KixQRoMdLgDLyxhsFVftkxqhZhyEfFZzsEy49LSojJ28vpHpBWLeCQBmnZ7JZ4C5yOQiqSQV/assBq2zJN2q+vCDp8qy5j1rED1SX5Ec7JpgpgnU4chLIf5Zn7bP/hNGT3pEYBuXeDXXN8ke1pcc3fc3m0FysDG0o56XVCUqImZ8Ezi8eujZciKDrWbtljhKTj7cnfuJx0sVHF6Bh5i4YfgA/Z+NL+MtH2EVIF67e6hEz6PWYTcoh3ybBaJfxb2FNvGJutNKg04GwMhYq6K2IddBt0fDiBt0SGM0oSBlUP3DKCUmXcf2a6ASbrcqv6Wz1jHt0pY4U8bEpg7qSbW3VDyvdPgyQ="
|
"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 = {
|
export const environment = {
|
||||||
production: true,
|
production: true,
|
||||||
|
debug: false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: false,
|
production: false,
|
||||||
|
debug: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -14,26 +14,28 @@ if (environment.production) {
|
|||||||
|
|
||||||
const debugService = new DebugService();
|
const debugService = new DebugService();
|
||||||
|
|
||||||
const consoleLog = console.log;
|
if (environment.debug) {
|
||||||
|
const consoleLog = console.log;
|
||||||
|
|
||||||
console.log = (...args) => {
|
console.log = (...args) => {
|
||||||
debugService.add({ type: 'log', args });
|
debugService.add({ type: 'log', args });
|
||||||
consoleLog(...args);
|
consoleLog(...args);
|
||||||
};
|
};
|
||||||
|
|
||||||
const consoleWarn = console.warn;
|
const consoleWarn = console.warn;
|
||||||
|
|
||||||
console.warn = (...args) => {
|
console.warn = (...args) => {
|
||||||
debugService.add({ type: 'warn', args });
|
debugService.add({ type: 'warn', args });
|
||||||
consoleWarn(...args);
|
consoleWarn(...args);
|
||||||
};
|
};
|
||||||
|
|
||||||
const consoleError = console.error;
|
const consoleError = console.error;
|
||||||
|
|
||||||
console.error = (...args) => {
|
console.error = (...args) => {
|
||||||
debugService.add({ type: 'error', args });
|
debugService.add({ type: 'error', args });
|
||||||
consoleError(...args);
|
consoleError(...args);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
platformBrowserDynamic([{ provide: DebugService, useValue: debugService }])
|
platformBrowserDynamic([{ provide: DebugService, useValue: debugService }])
|
||||||
.bootstrapModule(AppModule)
|
.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"
|
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"
|
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
|
Filter
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<shell-filter-overlay #filterOverlay class="relative">
|
<shell-filter-overlay #filterOverlay class="relative">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<button type="button" class="absolute top-4 right-4 text-cadet" (click)="closeFilterOverlay()">
|
<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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { SharedFilterOverlayModule } from '@shared/components/filter-overlay';
|
import { SharedFilterOverlayModule } from '@shared/components/filter-overlay';
|
||||||
import { UiFilterNextModule } from '@ui/filter';
|
import { UiFilterNextModule } from '@ui/filter';
|
||||||
import { UiIconModule } from '@ui/icon';
|
|
||||||
import { UiSpinnerModule } from '@ui/spinner';
|
import { UiSpinnerModule } from '@ui/spinner';
|
||||||
import { PriceUpdateListModule } from './price-update-list';
|
import { PriceUpdateListModule } from './price-update-list';
|
||||||
import { PriceUpdateComponent } from './price-update.component';
|
import { PriceUpdateComponent } from './price-update.component';
|
||||||
|
import { IconComponent } from '@shared/components/icon';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, PriceUpdateListModule, UiIconModule, UiFilterNextModule, SharedFilterOverlayModule, UiSpinnerModule],
|
imports: [CommonModule, PriceUpdateListModule, UiFilterNextModule, SharedFilterOverlayModule, UiSpinnerModule, IconComponent],
|
||||||
exports: [PriceUpdateComponent],
|
exports: [PriceUpdateComponent],
|
||||||
declarations: [PriceUpdateComponent],
|
declarations: [PriceUpdateComponent],
|
||||||
providers: [],
|
providers: [],
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
[routerLink]="closeFilterRoute"
|
[routerLink]="closeFilterRoute"
|
||||||
queryParamsHandling="preserve"
|
queryParamsHandling="preserve"
|
||||||
>
|
>
|
||||||
<ui-svg-icon icon="close" [size]="25"></ui-svg-icon>
|
<shared-icon icon="close" [size]="25"></shared-icon>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { UiIconModule } from '@ui/icon';
|
|
||||||
import { UiSpinnerModule } from '@ui/spinner';
|
import { UiSpinnerModule } from '@ui/spinner';
|
||||||
import { ArticleSearchFilterComponent } from './search-filter.component';
|
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({
|
@NgModule({
|
||||||
imports: [CommonModule, RouterModule, FilterNextModule, UiIconModule, UiSpinnerModule],
|
imports: [CommonModule, RouterModule, FilterModule, UiSpinnerModule, IconComponent],
|
||||||
exports: [ArticleSearchFilterComponent],
|
exports: [ArticleSearchFilterComponent],
|
||||||
declarations: [ArticleSearchFilterComponent],
|
declarations: [ArticleSearchFilterComponent],
|
||||||
providers: [],
|
providers: [],
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
[routerLink]="openFilterRoute"
|
[routerLink]="openFilterRoute"
|
||||||
queryParamsHandling="preserve"
|
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
|
Filter
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { UiIconModule } from '@ui/icon';
|
|
||||||
import { ArticleSearchMainComponent } from './search-main.component';
|
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 { RouterModule } from '@angular/router';
|
||||||
|
import { IconComponent } from '@shared/components/icon';
|
||||||
|
import { UiIconModule } from '@ui/icon';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, RouterModule, UiIconModule, FilterNextModule],
|
imports: [CommonModule, RouterModule, IconComponent, FilterModule, UiIconModule],
|
||||||
exports: [ArticleSearchMainComponent],
|
exports: [ArticleSearchMainComponent],
|
||||||
declarations: [ArticleSearchMainComponent],
|
declarations: [ArticleSearchMainComponent],
|
||||||
providers: [],
|
providers: [],
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
[routerLink]="filterRoute"
|
[routerLink]="filterRoute"
|
||||||
queryParamsHandling="preserve"
|
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
|
Filter
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ import { SearchResultItemLoadingComponent } from './search-result-item-loading.c
|
|||||||
import { SearchResultItemComponent } from './search-result-item.component';
|
import { SearchResultItemComponent } from './search-result-item.component';
|
||||||
import { ArticleSearchResultsComponent } from './search-results.component';
|
import { ArticleSearchResultsComponent } from './search-results.component';
|
||||||
import { SearchResultSelectedPipe } from './selected/search-result-selected.pipe';
|
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 { FocusSearchboxEvent } from '../focus-searchbox.event';
|
||||||
import { ArticleSearchMainAutocompleteProvider } from '../providers';
|
import { ArticleSearchMainAutocompleteProvider } from '../providers';
|
||||||
|
import { IconComponent } from '@shared/components/icon';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -32,7 +33,8 @@ import { ArticleSearchMainAutocompleteProvider } from '../providers';
|
|||||||
OrderByFilterModule,
|
OrderByFilterModule,
|
||||||
ScrollingModule,
|
ScrollingModule,
|
||||||
UiTooltipModule,
|
UiTooltipModule,
|
||||||
FilterNextModule,
|
FilterModule,
|
||||||
|
IconComponent,
|
||||||
],
|
],
|
||||||
exports: [ArticleSearchResultsComponent, SearchResultItemComponent],
|
exports: [ArticleSearchResultsComponent, SearchResultItemComponent],
|
||||||
declarations: [
|
declarations: [
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
[routerLink]="filterRoute"
|
[routerLink]="filterRoute"
|
||||||
queryParamsHandling="preserve"
|
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
|
Filter
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { UiFilterNextModule } from '@ui/filter';
|
|||||||
import { SharedFilterOverlayModule } from '@shared/components/filter-overlay';
|
import { SharedFilterOverlayModule } from '@shared/components/filter-overlay';
|
||||||
import { UiSpinnerModule } from '@ui/spinner';
|
import { UiSpinnerModule } from '@ui/spinner';
|
||||||
import { CustomerOrderSearchStore } from './customer-order-search.store';
|
import { CustomerOrderSearchStore } from './customer-order-search.store';
|
||||||
|
import { IconComponent } from '@shared/components/icon';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -19,6 +20,7 @@ import { CustomerOrderSearchStore } from './customer-order-search.store';
|
|||||||
SharedFilterOverlayModule,
|
SharedFilterOverlayModule,
|
||||||
UiSpinnerModule,
|
UiSpinnerModule,
|
||||||
OrderBranchIdInputComponent,
|
OrderBranchIdInputComponent,
|
||||||
|
IconComponent,
|
||||||
],
|
],
|
||||||
exports: [CustomerOrderSearchComponent],
|
exports: [CustomerOrderSearchComponent],
|
||||||
providers: [CustomerOrderSearchStore],
|
providers: [CustomerOrderSearchStore],
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
[routerLink]="filterRoute"
|
[routerLink]="filterRoute"
|
||||||
queryParamsHandling="preserve"
|
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
|
Filter
|
||||||
</a> -->
|
</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