Bearbeiten von Kundendaten

This commit is contained in:
Andreas Schickinger
2021-01-05 18:10:01 +01:00
parent f8210c3049
commit ea49c4ec12
23 changed files with 300 additions and 61 deletions

View File

@@ -86,7 +86,7 @@
</ng-container>
<ui-form-control label="Geburtsdatum (TT.MM.JJJJ)">
<input pagesDateOfBirth type="text" formControlName="dateOfBirth" tabindex="14" />
<input uiDateInput type="text" formControlName="dateOfBirth" tabindex="14" />
</ui-form-control>
<div class="center">

View File

@@ -5,8 +5,8 @@ import { ApplicationService } from '@core/application';
import { BreadcrumbService } from '@core/breadcrumb';
import { CrmCustomerService } from '@domain/crm';
import { CustomerDTO } from '@swagger/crm';
import { UiValidators } from '@ui/common';
import { UiModalService } from '@ui/modal';
import { CustomerCreateGuestComponent } from './customer-create-guest.component';
import { CustomerCreateComponentBase } from './customer-create.component';
@Component({
@@ -43,7 +43,7 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i
title: fb.control(customer?.title),
firstName: fb.control(customer?.firstName, [Validators.required]),
lastName: fb.control(customer?.lastName, [Validators.required]),
dateOfBirth: fb.control(customer?.dateOfBirth, [CustomerCreateGuestComponent.dateOfBirthValidator]),
dateOfBirth: fb.control(customer?.dateOfBirth, [UiValidators.date]),
address: fb.group({
street: fb.control(customer?.address?.street),
streetNumber: fb.control(customer?.address?.streetNumber),

View File

@@ -93,7 +93,7 @@
</ng-container>
<ui-form-control label="Geburtsdatum (TT.MM.JJJJ)">
<input type="text" formControlName="dateOfBirth" pagesDateOfBirth tabindex="14" />
<input type="text" formControlName="dateOfBirth" uiDateInput tabindex="14" />
</ui-form-control>
<div class="center">

View File

@@ -5,6 +5,7 @@ import { ApplicationService } from '@core/application';
import { BreadcrumbService } from '@core/breadcrumb';
import { CrmCustomerService } from '@domain/crm';
import { CustomerDTO } from '@swagger/crm';
import { UiValidators } from '@ui/common';
import { UiModalService } from '@ui/modal';
import { CustomerCreateComponentBase } from './customer-create.component';
@@ -42,7 +43,7 @@ export class CustomerCreateGuestComponent extends CustomerCreateComponentBase im
title: fb.control(customer?.title),
firstName: fb.control(customer?.firstName, [Validators.required]),
lastName: fb.control(customer?.lastName, [Validators.required]),
dateOfBirth: fb.control(customer?.dateOfBirth, [CustomerCreateGuestComponent.dateOfBirthValidator]),
dateOfBirth: fb.control(customer?.dateOfBirth, [UiValidators.date]),
address: fb.group({
street: fb.control(customer?.address?.street, [Validators.required]),
streetNumber: fb.control(customer?.address?.streetNumber, [Validators.required]),

View File

@@ -90,7 +90,7 @@
</ng-container>
<ui-form-control label="Geburtsdatum (TT.MM.JJJJ)">
<input pagesDateOfBirth type="text" formControlName="dateOfBirth" tabindex="14" />
<input uiDateInput type="text" formControlName="dateOfBirth" tabindex="14" />
</ui-form-control>
<div class="center">

View File

@@ -5,6 +5,7 @@ import { ApplicationService } from '@core/application';
import { BreadcrumbService } from '@core/breadcrumb';
import { CrmCustomerService } from '@domain/crm';
import { CustomerDTO } from '@swagger/crm';
import { UiValidators } from '@ui/common';
import { UiModalService } from '@ui/modal';
import { CustomerCreateComponentBase } from './customer-create.component';
@@ -42,7 +43,7 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
title: fb.control(customer?.title),
firstName: fb.control(customer?.firstName, [Validators.required]),
lastName: fb.control(customer?.lastName, [Validators.required]),
dateOfBirth: fb.control(customer?.dateOfBirth, [CustomerCreateComponentBase.dateOfBirthValidator]),
dateOfBirth: fb.control(customer?.dateOfBirth, [UiValidators.date]),
address: fb.group({
street: fb.control(customer?.address?.street, [Validators.required]),
streetNumber: fb.control(customer?.address?.streetNumber, [Validators.required]),

View File

@@ -11,16 +11,6 @@ import { BreadcrumbService } from '@core/breadcrumb';
import { ApplicationService } from '@core/application';
export abstract class CustomerCreateComponentBase {
static dateOfBirthValidator: ValidatorFn = (control): ValidationErrors | null => {
if (control.value) {
const match = /^(\d{4})(-(\d{2})){2}T((\d{2})(:|.)){3}(\d{3})Z$/g.test(control.value);
if (!match) {
return { dateOfBirth: 'Geburtsdatum ist ungültig' };
}
}
return null;
};
emailExistsValidator: AsyncValidatorFn = async (control): Promise<ValidationErrors | null> => {
if (control.value) {
if ((await this.customerService.emailExists(control.value).toPromise())?.result) {

View File

@@ -11,7 +11,6 @@ import { CustomerCreateBranchComponent } from './customer-create-branch.componen
import { CustomerCreateGuestComponent } from './customer-create-guest.component';
import { CustomerCreateOnlineComponent } from './customer-create-online.component';
import { CustomerCreateB2BComponent } from './customer-create-b2b.component';
import { DateOfBirthValueAccessorDirective } from './value-accessors/json-date.value-accessor';
import { AddressSelectionModalModule } from '../modals/address-selection-modal.module';
import { UiIconModule } from '@ui/icon';
@@ -34,7 +33,6 @@ import { UiIconModule } from '@ui/icon';
CustomerCreateGuestComponent,
CustomerCreateOnlineComponent,
CustomerCreateB2BComponent,
DateOfBirthValueAccessorDirective,
],
})
export class CustomerCreateModule {}

View File

@@ -0,0 +1,89 @@
<div class="header">
<h1>Kundendetails</h1>
</div>
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
<ui-form-control label="Anrede" variant="inline">
<ui-select formControlName="gender" tabindex="1">
<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 label="Titel" variant="inline">
<ui-select formControlName="title" tabindex="2">
<ui-select-option value="Dr." label="Dr."></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>
</ui-form-control>
<ui-form-control label="Vorname" variant="inline">
<input uiInput type="text" formControlName="firstName" />
</ui-form-control>
<ui-form-control label="Nachname" variant="inline">
<input uiInput type="text" formControlName="lastName" />
</ui-form-control>
<ui-form-control label="E-Mail" formGroupName="communicationDetails" variant="inline">
<input uiInput type="mail" formControlName="email" />
</ui-form-control>
<ng-container formGroupName="address">
<ui-form-control label="Straße" variant="inline">
<input uiInput type="text" formControlName="street" />
</ui-form-control>
<ui-form-control label="Hausnummer" variant="inline">
<input uiInput type="text" formControlName="streetNumber" />
</ui-form-control>
<ui-form-control label="PLZ" variant="inline">
<input uiInput type="text" formControlName="zipCode" />
</ui-form-control>
<ui-form-control label="Ort" variant="inline">
<input uiInput type="text" formControlName="city" />
</ui-form-control>
<ui-form-control label="Land" variant="inline">
<ui-select formControlName="country">
<ui-select-option
*ngFor="let country of countries$ | async"
[label]="country.name"
[value]="country.isO3166_A_3"
></ui-select-option>
</ui-select>
</ui-form-control>
</ng-container>
<ng-container formGroupName="communicationDetails">
<ui-form-control label="Festnetznummer" variant="inline">
<input uiInput type="tel" formControlName="phone" />
</ui-form-control>
<ui-form-control label="Mobilnummer" variant="inline">
<input uiInput type="tel" formControlName="mobile" />
</ui-form-control>
</ng-container>
<ui-form-control label="Geburtsdatum" variant="inline">
<input uiDateInput type="text" formControlName="dateOfBirth" />
</ui-form-control>
<ng-container formGroupName="organisation">
<ui-form-control label="Firmenname" variant="inline">
<input uiInput type="text" formControlName="name" />
</ui-form-control>
<ui-form-control label="Abteilung" variant="inline">
<input uiInput type="text" formControlName="department" />
</ui-form-control>
<ui-form-control label="USt ID" variant="inline">
<input uiInput type="text" formControlName="vatId" />
</ui-form-control>
</ng-container>
<ui-form-control label="Adresszusatz" formGroupName="address" variant="inline">
<input uiInput type="text" formControlName="info" />
</ui-form-control>
<div class="actions">
<button class="btn-cancel" type="button" (click)="cancel()">Abbrechen</button>
<button class="btn-save" type="submit" [disabled]="control.invalid || control.disabled" [ngSwitch]="control.enabled">
<ng-container *ngSwitchCase="true">
Speichern
</ng-container>
<ui-icon class="spin" icon="loading" size="18px" *ngSwitchCase="false"></ui-icon>
</button>
</div>
</form>

View File

@@ -0,0 +1,39 @@
:host {
@apply flex flex-col box-border shadow-card rounded-card;
}
.header {
@apply bg-white;
h1 {
@apply text-center text-card-heading my-16;
}
}
form {
@apply flex flex-col gap-px-2 bg-transparent;
ui-form-control {
@apply p-4 bg-white;
}
.actions {
@apply text-center my-8;
button {
@apply rounded-full outline-none p-4 text-cta-l border-brand border-solid px-px-25 py-px-15 mx-4 font-bold;
&.btn-cancel {
@apply text-brand bg-white;
}
&.btn-save {
@apply text-white bg-brand;
}
}
}
}
.spin {
@apply animate-spin;
}

View File

@@ -0,0 +1,97 @@
import { Location } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { CrmCustomerService } from '@domain/crm';
import { CountryDTO, CustomerDTO } from '@swagger/crm';
import { UiValidators } from '@ui/common';
import { Observable } from 'rxjs';
import { first, map, switchMap } from 'rxjs/operators';
@Component({
selector: 'page-customer-data-edit',
templateUrl: 'customer-data-edit.component.html',
styleUrls: ['customer-data-edit.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomerDataEditComponent implements OnInit {
customer$: Observable<CustomerDTO>;
customerId$: Observable<number>;
countries$: Observable<CountryDTO[]>;
control: FormGroup;
get customerId() {
return Number(this.activatedRoute.snapshot.params['customerId']);
}
constructor(
private customerService: CrmCustomerService,
private activatedRoute: ActivatedRoute,
private fb: FormBuilder,
private cdr: ChangeDetectorRef,
private location: Location
) {}
ngOnInit() {
this.countries$ = this.customerService.getCountries().pipe(map((c) => c.result));
this.customerId$ = this.activatedRoute.params.pipe(map((p) => Number(p['customerId'])));
this.customer$ = this.customerId$.pipe(
switchMap((id) => this.customerService.getCustomer(id, 2)),
map((cr) => cr.result)
);
this.initForm();
}
async initForm() {
const { fb } = this;
const customerDTO = await this.customer$.pipe(first()).toPromise();
this.control = fb.group({
gender: fb.control(customerDTO?.gender),
title: fb.control(customerDTO?.title),
lastName: fb.control(customerDTO?.lastName, [Validators.required]),
firstName: fb.control(customerDTO?.firstName, [Validators.required]),
dateOfBirth: fb.control(customerDTO?.dateOfBirth, UiValidators.date),
communicationDetails: fb.group({
email: fb.control(customerDTO?.communicationDetails?.email, [Validators.email]),
phone: fb.control(customerDTO?.communicationDetails?.phone),
mobile: fb.control(customerDTO?.communicationDetails?.mobile),
}),
organisation: fb.group({
name: fb.control(customerDTO?.organisation?.name),
vatId: fb.control(customerDTO?.organisation?.vatId),
department: fb.control(customerDTO?.organisation?.department),
}),
address: fb.group({
street: fb.control(customerDTO?.address?.street),
streetNumber: fb.control(customerDTO?.address?.streetNumber),
zipCode: fb.control(customerDTO?.address?.zipCode),
city: fb.control(customerDTO?.address?.city),
country: fb.control(customerDTO?.address?.country),
info: fb.control(customerDTO?.address?.info),
}),
});
this.cdr.markForCheck();
}
cancel() {
this.location.back();
}
async submit() {
if (!this.control.valid || !this.control.enabled) {
return;
}
this.control.disable();
try {
await this.customerService.patchCustomer(this.customerId, this.control.value).toPromise();
this.location.back();
} catch (err) {
this.control.enable();
console.error(err);
}
}
}

View File

@@ -48,7 +48,7 @@
</div>
<div class="field">
<span class="name">Geburtsdatum</span>
<span class="value">{{ customer?.dateOfBirth }}</span>
<span class="value">{{ customer?.dateOfBirth | date }}</span>
</div>
<div class="field">
<span class="name">Firmenname</span>

View File

@@ -6,9 +6,9 @@
<div class="card-customer-details">
<div class="header-container">
<h1 class="title">{{ (customerType$ | async) === 'b2b' ? 'Firmendetails' : 'Kundendetails' }}</h1>
<a class="button-customer-history">
<!-- <a class="button-customer-history">
Historie
</a>
</a> -->
</div>
<p class="info">Sind Ihre {{ (customerType$ | async) === 'b2b' ? 'Firmendaten' : 'Kundendaten' }} korrekt?</p>
<div class="tags">
@@ -26,7 +26,7 @@
<span class="name">Kundennummer</span>
<span class="value">{{ customer.customerNumber }}</span>
<div class="grow"></div>
<button class="edit-details" (click)="editDetails()">Bearbeiten</button>
<a [routerLink]="['/customer', customerId$ | async, 'edit']" class="edit-details" *ngIf="canEdit$ | async">Bearbeiten</a>
</div>
<!-- <div class="detail">
<span class="name">Kundennummer-Filiale</span>
@@ -39,33 +39,14 @@
</div>
</div>
<page-customer-data [customer]="customer$ | async" [customerType]="customerType$ | async"></page-customer-data>
<!-- <ng-container *ngIf="customerType$ | async; let customerType">
<page-customer-details-main *ngIf="customerType !== 'b2b'" [customer]="customer" [customerType]="customerType">
</page-customer-details-main>
<page-customer-details-main-b2b *ngIf="customerType === 'b2b'" [customer]="customer"> </page-customer-details-main-b2b>
</ng-container> -->
<div class="card-section-title">
<!-- <div class="card-section-title">
<h3>Rechnungsadresse</h3>
<button class="button-add-address">Hinzufügen</button>
</div>
<!-- <ui-inline-input [label]="payerToAddressString(payer)" *ngFor="let payer of customer.payers">
<input uiInput name="payer" type="radio" />
</ui-inline-input>
<div class="card-section-title">
<h3>Lieferadresse</h3>
<button class="button-add-address">Hinzufügen</button>
</div>
<ui-inline-input [label]="shippingAddressToAddressString(address?.data)" *ngFor="let address of customer.shippingAddresses">
<input uiInput name="shipping" type="radio" />
</ui-inline-input> -->
<div class="card-customer-footer">
</div> -->
<!-- <div class="card-customer-footer">
<button class="cta-to-cart">
Weiter zum Warenkorb
</button>
</div>
</div> -->
</ng-container>

View File

@@ -48,7 +48,7 @@
}
.edit-details {
@apply text-card-sub bg-transparent text-brand border-none font-bold rounded-full pr-0;
@apply text-card-sub bg-transparent text-brand border-none font-bold rounded-full pr-0 no-underline;
}
}
}

View File

@@ -1,5 +1,5 @@
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { ApplicationService } from '@core/application';
import { Breadcrumb, BreadcrumbService } from '@core/breadcrumb';
import { CrmCustomerService } from '@domain/crm';
@@ -19,6 +19,8 @@ export class CustomerDetailsComponent implements OnInit {
customerType$: Observable<'store' | 'b2b' | 'onlineshop'>;
canEdit$: Observable<boolean>;
private currentBreadcrumb: Breadcrumb;
payerToAddressString(payer: AssignedPayerDTO) {
@@ -35,7 +37,8 @@ export class CustomerDetailsComponent implements OnInit {
private activatedRoute: ActivatedRoute,
private customerDetailsService: CrmCustomerService,
private breadcrumb: BreadcrumbService,
private application: ApplicationService
private application: ApplicationService,
private router: Router
) {}
ngOnInit() {
@@ -58,6 +61,8 @@ export class CustomerDetailsComponent implements OnInit {
})
);
this.canEdit$ = this.customerType$.pipe(map((type) => type !== 'onlineshop'));
this.createBreadcrumb();
}
@@ -75,8 +80,4 @@ export class CustomerDetailsComponent implements OnInit {
updateBreadcrumbName(customer: CustomerDTO) {
this.breadcrumb.patchBreadcrumb(this.currentBreadcrumb.id, { name: `${customer?.firstName} ${customer?.lastName}` });
}
editDetails() {
console.log('edit');
}
}

View File

@@ -5,10 +5,25 @@ import { CustomerDetailsComponent } from './customer-details.component';
import { UiIconModule } from '@ui/icon';
import { CustomerDataComponent } from './customer-data/customer-data.component';
import { CustomerPipesModule } from '../pipes';
import { CustomerDataEditComponent } from './customer-data-edit/customer-data-edit.component';
import { ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { UiInputModule } from '@ui/input';
import { UiFormControlModule } from '@ui/form-control';
import { UiSelectModule } from '@ui/select';
@NgModule({
imports: [CommonModule, UiIconModule, CustomerPipesModule],
imports: [
CommonModule,
UiIconModule,
CustomerPipesModule,
ReactiveFormsModule,
RouterModule,
UiFormControlModule,
UiInputModule,
UiSelectModule,
],
exports: [CustomerDetailsComponent],
declarations: [CustomerDetailsComponent, CustomerDataComponent],
declarations: [CustomerDetailsComponent, CustomerDataComponent, CustomerDataEditComponent],
})
export class CustomerDetailsModule {}

View File

@@ -4,6 +4,7 @@ import { CustomerCreateB2BComponent } from './customer-create/customer-create-b2
import { CustomerCreateBranchComponent } from './customer-create/customer-create-branch.component';
import { CustomerCreateGuestComponent } from './customer-create/customer-create-guest.component';
import { CustomerCreateOnlineComponent } from './customer-create/customer-create-online.component';
import { CustomerDataEditComponent } from './customer-details/customer-data-edit/customer-data-edit.component';
import { CustomerDetailsComponent } from './customer-details/customer-details.component';
import { CustomerSearchComponent } from './customer-search/customer-search.component';
import { CustomerSearchMainComponent } from './customer-search/search-main/search-main.component';
@@ -38,6 +39,10 @@ const routes: Routes = [
path: ':customerId',
component: CustomerDetailsComponent,
},
{
path: ':customerId/edit',
component: CustomerDataEditComponent,
},
],
},
];

View File

@@ -2,4 +2,5 @@
export * from './click-outside.directive';
export * from './common.module';
export * from './is-in-viewport.directive';
export * from './validators';
// end:ng42.barrel

View File

@@ -0,0 +1,11 @@
import { ValidationErrors } from '@angular/forms';
export function date(control): ValidationErrors | null {
if (control.value) {
const match = /^(\d{4})(-(\d{2})){2}T((\d{2})(:|.?)){3}(\d{3})?Z$/g.test(control.value);
if (!match) {
return { dateOfBirth: 'Geburtsdatum ist ungültig' };
}
}
return null;
}

View File

@@ -0,0 +1,3 @@
import { date } from './date.validator';
export const UiValidators = { date };

View File

@@ -115,3 +115,9 @@ button.clear {
}
}
}
:host[variant='inline'] {
.input-wrapper {
@apply gap-8;
}
}

View File

@@ -3,20 +3,20 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { UiFormControlDirective } from '@ui/form-control';
@Directive({
selector: 'input[pagesDateOfBirth]',
selector: 'input[uiDateInput]',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DateOfBirthValueAccessorDirective),
useExisting: forwardRef(() => UiDateInputDirective),
multi: true,
},
{
provide: UiFormControlDirective,
useExisting: DateOfBirthValueAccessorDirective,
useExisting: UiDateInputDirective,
},
],
})
export class DateOfBirthValueAccessorDirective extends UiFormControlDirective<any> implements ControlValueAccessor {
export class UiDateInputDirective extends UiFormControlDirective<any> implements ControlValueAccessor {
@Input()
@HostBinding('attr.type')
type: string;

View File

@@ -1,13 +1,14 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { UiDateInputDirective } from './ui-date-input.directive';
import { UiInputDirective } from './ui-input.directive';
import { UiRadioInputDirective } from './ui-radio-input.directive';
@NgModule({
imports: [CommonModule],
exports: [UiInputDirective, UiRadioInputDirective],
declarations: [UiInputDirective, UiRadioInputDirective],
exports: [UiInputDirective, UiRadioInputDirective, UiDateInputDirective],
declarations: [UiInputDirective, UiRadioInputDirective, UiDateInputDirective],
providers: [],
})
export class UiInputModule {}