Merged PR 1621: #4272 Menü zeigt Bestellungen, Kundenkarte und Details an

#4272 Menü zeigt Bestellungen, Kundenkarte und Details an
This commit is contained in:
Lorenz Hilpert
2023-09-04 13:17:47 +00:00
committed by Nino Righi
parent 5167ba21a6
commit 564afb7e32
19 changed files with 224 additions and 62 deletions

View File

@@ -271,6 +271,11 @@
"name": "calendar-today",
"data": "M180-80q-24 0-42-18t-18-42v-620q0-24 18-42t42-18h65v-60h65v60h340v-60h65v60h65q24 0 42 18t18 42v620q0 24-18 42t-42 18H180Zm0-60h600v-430H180v430Zm0-490h600v-130H180v130Zm0 0v-130 130Z",
"viewBox": "0 -960 960 960"
},
{
"name": "apps",
"data": "M226-160q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19Zm254 0q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19Zm254 0q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19ZM226-414q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19Zm254 0q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19Zm254 0q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19ZM226-668q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19Zm254 0q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19Zm254 0q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19Z",
"viewBox": "0 -960 960 960"
}
],

View File

@@ -0,0 +1,3 @@
:host {
@apply inline-block;
}

View File

@@ -0,0 +1,49 @@
<button
type="button"
class="px-2 py-3 bg-[#C6CBD0] rounded flex flex-row items-center open:bg-[#596470] open:text-white z-dropdown"
[cdkMenuTriggerFor]="navMenu"
#menuTrigger="cdkMenuTriggerFor"
[class.open]="menuTrigger.isOpen()"
>
<shared-icon icon="apps" [size]="24"></shared-icon>
<shared-icon [icon]="menuTrigger.isOpen() ? 'arrow-drop-up' : 'arrow-drop-down'" [size]="24"></shared-icon>
</button>
<ng-template #navMenu>
<div class="pt-1">
<shared-menu>
<a
sharedMenuItem
*ngIf="customerDetailsRoute$ | async; let customerDetailsRoute"
[routerLink]="customerDetailsRoute.path"
[queryParams]="customerDetailsRoute.queryParams"
[queryParamsHandling]="'merge'"
>Kundendetails</a
>
<a
sharedMenuItem
*ngIf="ordersRoute$ | async; let ordersRoute"
[routerLink]="ordersRoute.path"
[queryParams]="ordersRoute.queryParams"
[queryParamsHandling]="'merge'"
>Bestellungen</a
>
<a
sharedMenuItem
*ngIf="kundenkarteRoute$ | async; let kundenkarteRoute"
[routerLink]="kundenkarteRoute.path"
[queryParams]="kundenkarteRoute.queryParams"
[queryParamsHandling]="'merge'"
>Kundenkarte</a
>
<a
sharedMenuItem
*ngIf="historyRoute$ | async; let historyRoute"
[routerLink]="historyRoute.path"
[queryParams]="historyRoute.queryParams"
[queryParamsHandling]="'merge'"
>Historie</a
>
</shared-menu>
</div>
</ng-template>

View File

@@ -0,0 +1,111 @@
import { BooleanInput, NumberInput, coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
import { CdkMenuModule } from '@angular/cdk/menu';
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
import { IconComponent } from '@shared/components/icon';
import { SharedMenuModule } from '@shared/components/menu';
import { combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { CustomerSearchNavigation } from '../../navigations';
import { ComponentStore } from '@ngrx/component-store';
import { RouterLink } from '@angular/router';
import { AsyncPipe, NgIf } from '@angular/common';
export interface CustomerMenuComponentState {
customerId?: number;
processId?: number;
hasCustomerCard?: boolean;
showCustomerDetails: boolean;
showCustomerOrders: boolean;
showCustomerHistory: boolean;
showCustomerCard: boolean;
}
@Component({
selector: 'page-customer-menu',
templateUrl: 'customer-menu.component.html',
styleUrls: ['customer-menu.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'page-customer-menu' },
standalone: true,
imports: [CdkMenuModule, SharedMenuModule, IconComponent, RouterLink, NgIf, AsyncPipe],
})
export class CustomerMenuComponent extends ComponentStore<CustomerMenuComponentState> {
@Input() set customerId(value: NumberInput) {
this.patchState({ customerId: coerceNumberProperty(value) });
}
@Input() set hasCustomerCard(value: BooleanInput) {
this.patchState({ hasCustomerCard: coerceBooleanProperty(value) });
}
@Input() set processId(value: NumberInput) {
this.patchState({ processId: coerceNumberProperty(value) });
}
@Input() set showCustomerDetails(value: BooleanInput) {
this.patchState({ showCustomerDetails: coerceBooleanProperty(value) });
}
@Input() set showCustomerOrders(value: BooleanInput) {
this.patchState({ showCustomerOrders: coerceBooleanProperty(value) });
}
@Input() set showCustomerHistory(value: BooleanInput) {
this.patchState({ showCustomerHistory: coerceBooleanProperty(value) });
}
@Input() set showCustomerCard(value: BooleanInput) {
this.patchState({ showCustomerCard: coerceBooleanProperty(value) });
}
readonly customerId$ = this.select((state) => state.customerId);
readonly processId$ = this.select((state) => state.processId);
readonly hasCustomerCard$ = this.select((state) => state.hasCustomerCard);
readonly showCustomerDetails$ = this.select((state) => state.showCustomerDetails);
readonly showCustomerOrders$ = this.select((state) => state.showCustomerOrders);
readonly showCustomerHistory$ = this.select((state) => state.showCustomerHistory);
readonly showCustomerCard$ = this.select((state) => state.showCustomerCard);
historyRoute$ = combineLatest([this.showCustomerHistory$, this.processId$, this.customerId$]).pipe(
map(
([showCustomerHistory, processId, customerId]) =>
showCustomerHistory && processId && customerId && this._navigation.historyRoute({ processId, customerId })
)
);
ordersRoute$ = combineLatest([this.showCustomerOrders$, this.processId$, this.customerId$]).pipe(
map(
([showCustomerOrders, processId, customerId]) =>
showCustomerOrders && processId && customerId && this._navigation.ordersRoute({ processId, customerId })
)
);
kundenkarteRoute$ = combineLatest([this.showCustomerCard$, this.hasCustomerCard$, this.processId$, this.customerId$]).pipe(
map(
([showCustomerCard, hasCustomerCard, processId, customerId]) =>
showCustomerCard && hasCustomerCard && processId && customerId && this._navigation.kundenkarteRoute({ processId, customerId })
)
);
customerDetailsRoute$ = combineLatest([this.showCustomerDetails$, this.processId$, this.customerId$]).pipe(
map(
([showCustomerDetails, processId, customerId]) =>
showCustomerDetails && processId && customerId && this._navigation.detailsRoute({ processId, customerId })
)
);
constructor(private _navigation: CustomerSearchNavigation) {
super({
showCustomerCard: true,
showCustomerDetails: true,
showCustomerHistory: true,
showCustomerOrders: true,
});
}
}

View File

@@ -0,0 +1 @@
export * from './customer-menu.component';

View File

@@ -1,31 +1,13 @@
<shared-loader [loading]="fetching$ | async" background="true" spinnerSize="32">
<div class="overflow-scroll max-h-[calc(100vh-15rem)]">
<div class="customer-details-header grid grid-flow-row pt-1 px-1 pb-6">
<div class="customer-details-header-actions flex flex-row justify-end pt-1 px-1">
<a
*ngIf="ordersRoute$ | async; let ordersRoute"
class="btn btn-label font-bold text-brand"
[routerLink]="ordersRoute.path"
[queryParams]="ordersRoute.queryParams"
[queryParamsHandling]="'merge'"
>Bestellungen</a
>
<a
*ngIf="kundenkarteRoute$ | async; let kundenkarteRoute"
class="btn btn-label font-bold text-brand"
[routerLink]="kundenkarteRoute.path"
[queryParams]="kundenkarteRoute.queryParams"
[queryParamsHandling]="'merge'"
>Kundenkarte</a
>
<a
*ngIf="historyRoute$ | async; let historyRoute"
class="btn btn-label font-bold text-brand"
[routerLink]="historyRoute.path"
[queryParams]="historyRoute.queryParams"
[queryParamsHandling]="'merge'"
>Historie</a
>
<div class="customer-details-header grid grid-flow-row pb-6">
<div class="customer-details-header-actions flex flex-row justify-end pt-4 px-4">
<page-customer-menu
[customerId]="customerId$ | async"
[processId]="processId$ | async"
[hasCustomerCard]="hasKundenkarte$ | async"
[showCustomerDetails]="false"
></page-customer-menu>
</div>
<div class="customer-details-header-body text-center -mt-3">
<h1 class="text-[1.625rem] font-bold">

View File

@@ -1,9 +1,8 @@
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core';
import { Subject, combineLatest } from 'rxjs';
import { debounceTime, filter, first, map, share, switchMap, takeUntil, tap, throttleTime } from 'rxjs/operators';
import { first, map, switchMap, takeUntil } from 'rxjs/operators';
import { CustomerSearchNavigation } from '../../navigations';
import { CustomerSearchStore } from '../store';
import { CrmCustomerService } from '@domain/crm';
import { ShippingAddressDTO, NotificationChannel, ShoppingCartDTO, PayerDTO, BuyerDTO } from '@swagger/checkout';
import { DomainCheckoutService } from '@domain/checkout';
import { CantAddCustomerToCartData, CantAddCustomerToCartModalComponent, CantSelectGuestModalComponent } from '../../modals';
@@ -13,7 +12,6 @@ import { ApplicationService } from '@core/application';
import { CheckoutNavigationService, ProductCatalogNavigationService } from '@shared/services';
import { Router } from '@angular/router';
import { log, logAsync } from '@utils/common';
import { isBoolean } from 'lodash';
const GENDER_MAP = {
2: 'Herr',

View File

@@ -8,6 +8,7 @@ import { DetailsMainViewBillingAddressesComponent } from './details-main-view-bi
import { DetailsMainViewDeliveryAddressesComponent } from './details-main-view-delivery-addresses/details-main-view-delivery-addresses.component';
import { LoaderComponent } from '@shared/components/loader';
import { IconComponent } from '@shared/components/icon';
import { CustomerMenuComponent } from '../../components/customer-menu';
@NgModule({
imports: [
@@ -19,6 +20,7 @@ import { IconComponent } from '@shared/components/icon';
DetailsMainViewDeliveryAddressesComponent,
LoaderComponent,
IconComponent,
CustomerMenuComponent,
],
exports: [CustomerDetailsViewMainComponent],
declarations: [CustomerDetailsViewMainComponent],

View File

@@ -2,16 +2,13 @@
<shared-loader [loading]="fetching$ | async" [background]="true" [spinnerSize]="48">
<div>
<div class="customer-history-header">
<div class="customer-history-header-actions flex flex-row justify-end pt-1 px-1">
<a
*ngIf="detailsRoute$ | async; let route"
[routerLink]="route.path"
[queryParams]="route.queryParams"
[queryParamsHandling]="'merge'"
class="btn btn-label"
>
<ui-icon [icon]="'close'"></ui-icon>
</a>
<div class="customer-history-header-actions flex flex-row justify-end pt-4 px-4">
<page-customer-menu
[customerId]="customerId$ | async"
[processId]="processId$ | async"
[hasCustomerCard]="hasKundenkarte$ | async"
[showCustomerHistory]="false"
></page-customer-menu>
</div>
<div class="customer-history-header-body text-center -mt-3">
<h1 class="text-[1.625rem] font-bold">Historie</h1>

View File

@@ -27,6 +27,10 @@ export class CustomerHistoryMainViewComponent extends ComponentStore<CustomerHis
customerId$ = this._store.customerId$;
hasKundenkarte$ = combineLatest([this._store.isKundenkarte$, this._store.isOnlineKontoMitKundenkarte$]).pipe(
map(([isKundenkarte, isOnlineKontoMitKundenkarte]) => isKundenkarte || isOnlineKontoMitKundenkarte)
);
customer$ = this._store.customer$;
detailsRoute$ = combineLatest([this.processId$, this.customerId$]).pipe(
@@ -65,6 +69,7 @@ export class CustomerHistoryMainViewComponent extends ComponentStore<CustomerHis
};
handleFetchHistoryError = (err: any) => {
this.patchState({ fetching: false });
console.error(err);
};

View File

@@ -6,9 +6,10 @@ import { CustomerHistoryMainViewComponent } from './history-main-view.component'
import { UiIconModule } from '@ui/icon';
import { RouterModule } from '@angular/router';
import { LoaderModule } from '@shared/components/loader';
import { CustomerMenuComponent } from '../../components/customer-menu';
@NgModule({
imports: [CommonModule, RouterModule, SharedHistoryListModule, UiIconModule, LoaderModule],
imports: [CommonModule, RouterModule, SharedHistoryListModule, UiIconModule, LoaderModule, CustomerMenuComponent],
exports: [CustomerHistoryMainViewComponent],
declarations: [CustomerHistoryMainViewComponent],
})

View File

@@ -1,12 +1,5 @@
<div class="flex flex-row justify-end -mt-4 -mr-2">
<a
*ngIf="detailsRoute$ | async; let detailsRoute"
[routerLink]="detailsRoute.path"
[queryParams]="detailsRoute.urlTree.queryParams"
[queryParamsHandling]="'merge'"
>
<shared-icon icon="close" [size]="32"></shared-icon>
</a>
<div class="flex flex-row justify-end -mt-2">
<page-customer-menu [customerId]="customerId$ | async" [processId]="processId$ | async" [showCustomerCard]="false"></page-customer-menu>
</div>
<h1 class="text-center text-2xl font-bold">Kundenkarte</h1>
<p class="text-center text-xl" *ngIf="!(noDataFound$ | async)">

View File

@@ -9,6 +9,7 @@ import { AsyncPipe, NgFor, NgIf } from '@angular/common';
import { IconComponent } from '@shared/components/icon';
import { CustomerSearchNavigation } from '../../navigations';
import { BonusCardInfoDTO } from '@swagger/crm';
import { CustomerMenuComponent } from '../../components/customer-menu';
@Component({
selector: 'page-customer-kundenkarte-main-view',
@@ -17,13 +18,15 @@ import { BonusCardInfoDTO } from '@swagger/crm';
changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'page-customer-kundenkarte-main-view' },
standalone: true,
imports: [KundenkarteComponent, NgFor, AsyncPipe, NgIf, IconComponent, RouterLink],
imports: [CustomerMenuComponent, KundenkarteComponent, NgFor, AsyncPipe, NgIf, IconComponent, RouterLink],
})
export class KundenkarteMainViewComponent implements OnInit, OnDestroy {
private _onDestroy$ = new Subject<void>();
customerId$ = this._activatedRoute.params.pipe(map((params) => params.customerId));
processId$ = this._store.processId$;
kundenkarte$ = this.customerId$.pipe(
switchMap((customerId) =>
this._customerService.getCustomerCard(customerId).pipe(

View File

@@ -1,13 +1,11 @@
<div class="wrapper">
<div class="flex flex-row justify-end -mt-4 -mr-2">
<a
*ngIf="detailsRoute$ | async; let detailsRoute"
[routerLink]="detailsRoute.path"
[queryParams]="detailsRoute.urlTree.queryParams"
[queryParamsHandling]="'merge'"
>
<shared-icon icon="close" [size]="32"></shared-icon>
</a>
<page-customer-menu
[customerId]="customerId$ | async"
[processId]="processId$ | async"
[hasCustomerCard]="hasKundenkarte$ | async"
[showCustomerOrders]="false"
></page-customer-menu>
</div>
<h1 class="text-2xl text-center font-bold mb-4">Bestellungen</h1>
<p class="text-xl text-center">

View File

@@ -8,6 +8,7 @@ import { RouterLink } from '@angular/router';
import { IconComponent } from '@shared/components/icon';
import { LoaderComponent } from '@shared/components/loader';
import { CustomerOrderListItemComponent } from './order-list-item/order-list-item.component';
import { CustomerMenuComponent } from '../../components/customer-menu';
@Component({
selector: 'page-customer-orders-main-view',
@@ -16,11 +17,19 @@ import { CustomerOrderListItemComponent } from './order-list-item/order-list-ite
changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'page-customer-orders-main-view' },
standalone: true,
imports: [AsyncPipe, NgFor, NgIf, RouterLink, IconComponent, LoaderComponent, CustomerOrderListItemComponent],
imports: [CustomerMenuComponent, AsyncPipe, NgFor, NgIf, RouterLink, IconComponent, LoaderComponent, CustomerOrderListItemComponent],
})
export class CustomerOrdersMainViewComponent implements OnInit, OnDestroy {
private _onDestroy = new Subject<void>();
processId$ = this._store.processId$;
customerId$ = this._store.customerId$;
hasKundenkarte$ = combineLatest([this._store.isKundenkarte$, this._store.isOnlineKontoMitKundenkarte$]).pipe(
map(([isKundenkarte, isOnlineKontoMitKundenkarte]) => isKundenkarte || isOnlineKontoMitKundenkarte)
);
orders$ = this._store.customerOrders$;
fetching$ = this._store.fetchingCustomerOrders$;

View File

@@ -1,7 +1,7 @@
import { Highlightable } from '@angular/cdk/a11y';
import { Directive, ElementRef, HostListener, Input, Renderer2 } from '@angular/core';
@Directive({ selector: '[menuItem]', host: { class: 'menu-item', role: 'menuitem', tabindex: '-1' } })
@Directive({ selector: '[menuItem], [sharedMenuItem]', host: { class: 'menu-item', role: 'menuitem', tabindex: '-1' } })
export class MenuItemDirective implements Highlightable {
private _onClick = (_: MenuItemDirective) => {};

View File

@@ -2,7 +2,7 @@ import { Component, ContentChildren, QueryList } from '@angular/core';
import { MenuItemDirective } from './menu-item.directive';
@Component({
selector: 'menu',
selector: 'menu, shared-menu',
template: `<ng-content [selector]="[menuItem]"></ng-content>`,
host: { class: 'menu', role: 'menu' },
exportAs: 'menu',

View File

@@ -7,7 +7,7 @@ module.exports = plugin(function ({ addComponents, theme, addBase, matchUtilitie
'--menu-content': theme('colors.components.menu.content'),
'--menu-item-height': theme('spacing.12'),
'--menu-item-padding': `${theme('spacing.1')} ${theme('spacing.3')}`,
'--menu-border-radius': theme('borderRadius.menu'),
'--menu-border-radius': theme('borderRadius.DEFAULT'),
'--menu-item-hover-background': theme('colors.components.menu.hover.DEFAULT'),
'--menu-item-hover-content': theme('colors.components.menu.hover.content'),
'--menu-item-hover-border': theme('colors.components.menu.hover.border'),

View File

@@ -1,3 +1,5 @@
const plugin = require('tailwindcss/plugin');
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./apps/**/*.{html,ts}'],
@@ -202,5 +204,8 @@ module.exports = {
require('./tailwind-plugins/select-bullet.plugin.js'),
require('./tailwind-plugins/section.plugin.js'),
require('./tailwind-plugins/typography.plugin.js'),
plugin(({ addVariant }) => {
addVariant('open', '&.open');
}),
],
};