mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merged PR 1961: feat(checkout-reward): implement reward catalog customer integration and purc...
feat(checkout-reward): implement reward catalog customer integration and purchase flow - Add customer card resource and display in reward header with reset functionality - Implement shopping cart creation and management for reward purchases - Add purchase options modal integration with redemption points support - Extract route helper for customer navigation with proper query params - Update checkout metadata service constants with proper namespacing - Add reward context initialization for tab metadata - Improve component styling and layout for reward action buttons - Fix customer facade method signature to require AbortSignal parameter The reward catalog now supports full customer workflow from selection through purchase options with proper state management and navigation. Ref: #5263, #5358
This commit is contained in:
committed by
Lorenz Hilpert
parent
c745f82f3a
commit
9d57ebf376
@@ -282,7 +282,7 @@ export class PurchaseOptionsListItemComponent
|
||||
return (
|
||||
this.useRedemptionPoints() &&
|
||||
this.isReservePurchaseOption() &&
|
||||
this.availability().inStock < 2
|
||||
(!this.availability() || this.availability().inStock < 2)
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@ export class PurchaseOptionsModalService {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
return this.#customerFacade.fetchCustomer({ customerId });
|
||||
return this.#customerFacade.fetchCustomer(
|
||||
{ customerId },
|
||||
new AbortController().signal,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, Host, inject } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
Host,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { CustomerSearchStore } from '../../store';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { debounceTime, map, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { Observable, Subject, combineLatest } from 'rxjs';
|
||||
import { AssignedPayerDTO, CustomerDTO, ListResponseArgsOfAssignedPayerDTO } from '@generated/swagger/crm-api';
|
||||
import {
|
||||
AssignedPayerDTO,
|
||||
CustomerDTO,
|
||||
ListResponseArgsOfAssignedPayerDTO,
|
||||
} from '@generated/swagger/crm-api';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { CustomerPipesModule } from '@shared/pipes/customer';
|
||||
import { ComponentStore } from '@ngrx/component-store';
|
||||
@@ -14,6 +25,8 @@ import { CustomerSearchNavigation } from '@shared/services/navigation';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { CustomerDetailsViewMainComponent } from '../details-main-view.component';
|
||||
import { PayerDTO } from '@generated/swagger/checkout-api';
|
||||
import { CrmTabMetadataService } from '@isa/crm/data-access';
|
||||
import { injectTabId } from '@isa/core/tabs';
|
||||
|
||||
interface DetailsMainViewBillingAddressesComponentState {
|
||||
assignedPayers: AssignedPayerDTO[];
|
||||
@@ -32,6 +45,9 @@ export class DetailsMainViewBillingAddressesComponent
|
||||
extends ComponentStore<DetailsMainViewBillingAddressesComponentState>
|
||||
implements OnInit, OnDestroy
|
||||
{
|
||||
tabId = injectTabId();
|
||||
crmTabMetadataService = inject(CrmTabMetadataService);
|
||||
|
||||
private _host = inject(CustomerDetailsViewMainComponent, { host: true });
|
||||
private _store = inject(CustomerSearchStore);
|
||||
private _customerService = inject(CrmCustomerService);
|
||||
@@ -42,13 +58,20 @@ export class DetailsMainViewBillingAddressesComponent
|
||||
|
||||
selectedPayer$ = this.select((state) => state.selectedPayer);
|
||||
|
||||
isNotBusinessKonto$ = this._store.isBusinessKonto$.pipe(map((isBusinessKonto) => !isBusinessKonto));
|
||||
isNotBusinessKonto$ = this._store.isBusinessKonto$.pipe(
|
||||
map((isBusinessKonto) => !isBusinessKonto),
|
||||
);
|
||||
|
||||
showCustomerAddress$ = combineLatest([
|
||||
this._store.isBusinessKonto$,
|
||||
this._store.isMitarbeiter$,
|
||||
this._store.isKundenkarte$,
|
||||
]).pipe(map(([isBusinessKonto, isMitarbeiter, isKundenkarte]) => isBusinessKonto || isMitarbeiter || isKundenkarte));
|
||||
]).pipe(
|
||||
map(
|
||||
([isBusinessKonto, isMitarbeiter, isKundenkarte]) =>
|
||||
isBusinessKonto || isMitarbeiter || isKundenkarte,
|
||||
),
|
||||
);
|
||||
|
||||
get showCustomerAddress() {
|
||||
return this._store.isBusinessKonto || this._store.isMitarbeiter;
|
||||
@@ -65,14 +88,22 @@ export class DetailsMainViewBillingAddressesComponent
|
||||
),
|
||||
);
|
||||
|
||||
canEditAddress$ = combineLatest([this._store.isKundenkarte$]).pipe(map(([isKundenkarte]) => isKundenkarte));
|
||||
canEditAddress$ = combineLatest([this._store.isKundenkarte$]).pipe(
|
||||
map(([isKundenkarte]) => isKundenkarte),
|
||||
);
|
||||
|
||||
customer$ = this._store.customer$;
|
||||
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
editRoute$ = combineLatest([this._store.processId$, this._store.customerId$, this._store.isBusinessKonto$]).pipe(
|
||||
map(([processId, customerId, isB2b]) => this._navigation.editRoute({ processId, customerId, isB2b })),
|
||||
editRoute$ = combineLatest([
|
||||
this._store.processId$,
|
||||
this._store.customerId$,
|
||||
this._store.isBusinessKonto$,
|
||||
]).pipe(
|
||||
map(([processId, customerId, isB2b]) =>
|
||||
this._navigation.editRoute({ processId, customerId, isB2b }),
|
||||
),
|
||||
);
|
||||
|
||||
addBillingAddressRoute$ = combineLatest([
|
||||
@@ -81,7 +112,9 @@ export class DetailsMainViewBillingAddressesComponent
|
||||
this._store.customerId$,
|
||||
]).pipe(
|
||||
map(([canAddNewAddress, processId, customerId]) =>
|
||||
canAddNewAddress ? this._navigation.addBillingAddressRoute({ processId, customerId }) : undefined,
|
||||
canAddNewAddress
|
||||
? this._navigation.addBillingAddressRoute({ processId, customerId })
|
||||
: undefined,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -116,6 +149,10 @@ export class DetailsMainViewBillingAddressesComponent
|
||||
.subscribe(([selectedPayer, customer]) => {
|
||||
if (selectedPayer) {
|
||||
this._host.setPayer(this._createPayerFromCrmPayerDTO(selectedPayer));
|
||||
this.crmTabMetadataService.setSelectedPayerAddressId(
|
||||
this.tabId(),
|
||||
selectedPayer?.payer?.id,
|
||||
);
|
||||
} else if (this.showCustomerAddress) {
|
||||
this._host.setPayer(this._createPayerFormCustomer(customer));
|
||||
}
|
||||
@@ -133,7 +170,9 @@ export class DetailsMainViewBillingAddressesComponent
|
||||
title: payer.title,
|
||||
firstName: payer.firstName,
|
||||
lastName: payer.lastName,
|
||||
communicationDetails: payer.communicationDetails ? { ...payer.communicationDetails } : undefined,
|
||||
communicationDetails: payer.communicationDetails
|
||||
? { ...payer.communicationDetails }
|
||||
: undefined,
|
||||
organisation: payer.organisation ? { ...payer.organisation } : undefined,
|
||||
address: payer.address ? { ...payer.address } : undefined,
|
||||
source: payer.id,
|
||||
@@ -150,8 +189,12 @@ export class DetailsMainViewBillingAddressesComponent
|
||||
title: customer.title,
|
||||
firstName: customer.firstName,
|
||||
lastName: customer.lastName,
|
||||
communicationDetails: customer.communicationDetails ? { ...customer.communicationDetails } : undefined,
|
||||
organisation: customer.organisation ? { ...customer.organisation } : undefined,
|
||||
communicationDetails: customer.communicationDetails
|
||||
? { ...customer.communicationDetails }
|
||||
: undefined,
|
||||
organisation: customer.organisation
|
||||
? { ...customer.organisation }
|
||||
: undefined,
|
||||
address: customer.address ? { ...customer.address } : undefined,
|
||||
};
|
||||
}
|
||||
@@ -166,26 +209,36 @@ export class DetailsMainViewBillingAddressesComponent
|
||||
switchMap((customerId) =>
|
||||
this._customerService
|
||||
.getAssignedPayers({ customerId })
|
||||
.pipe(tapResponse(this.handleLoadAssignedPayersResponse, this.handleLoadAssignedPayersError)),
|
||||
.pipe(
|
||||
tapResponse(
|
||||
this.handleLoadAssignedPayersResponse,
|
||||
this.handleLoadAssignedPayersError,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
handleLoadAssignedPayersResponse = (response: ListResponseArgsOfAssignedPayerDTO) => {
|
||||
const selectedPayer = response.result.reduce<AssignedPayerDTO>((prev, curr) => {
|
||||
if (!prev) {
|
||||
handleLoadAssignedPayersResponse = (
|
||||
response: ListResponseArgsOfAssignedPayerDTO,
|
||||
) => {
|
||||
const selectedPayer = response.result.reduce<AssignedPayerDTO>(
|
||||
(prev, curr) => {
|
||||
if (!prev) {
|
||||
return curr;
|
||||
}
|
||||
|
||||
const prevDate = new Date(prev?.isDefault ?? 0);
|
||||
const currDate = new Date(curr?.isDefault ?? 0);
|
||||
|
||||
if (prevDate > currDate) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
const prevDate = new Date(prev?.isDefault ?? 0);
|
||||
const currDate = new Date(curr?.isDefault ?? 0);
|
||||
|
||||
if (prevDate > currDate) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
return curr;
|
||||
}, undefined);
|
||||
},
|
||||
undefined,
|
||||
);
|
||||
|
||||
this.patchState({
|
||||
assignedPayers: response.result,
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, Host, inject } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
Host,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { CustomerSearchStore } from '../../store';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { map, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { Observable, Subject, combineLatest } from 'rxjs';
|
||||
import { CustomerDTO, ListResponseArgsOfAssignedPayerDTO, ShippingAddressDTO } from '@generated/swagger/crm-api';
|
||||
import {
|
||||
CustomerDTO,
|
||||
ListResponseArgsOfAssignedPayerDTO,
|
||||
ShippingAddressDTO,
|
||||
} from '@generated/swagger/crm-api';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { CustomerPipesModule } from '@shared/pipes/customer';
|
||||
import { ComponentStore } from '@ngrx/component-store';
|
||||
@@ -13,6 +24,8 @@ import { UiModalService } from '@ui/modal';
|
||||
import { CustomerSearchNavigation } from '@shared/services/navigation';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { CustomerDetailsViewMainComponent } from '../details-main-view.component';
|
||||
import { CrmTabMetadataService } from '@isa/crm/data-access';
|
||||
import { injectTabId } from '@isa/core/tabs';
|
||||
|
||||
interface DetailsMainViewDeliveryAddressesComponentState {
|
||||
shippingAddresses: ShippingAddressDTO[];
|
||||
@@ -31,6 +44,9 @@ export class DetailsMainViewDeliveryAddressesComponent
|
||||
extends ComponentStore<DetailsMainViewDeliveryAddressesComponentState>
|
||||
implements OnInit, OnDestroy
|
||||
{
|
||||
tabId = injectTabId();
|
||||
crmTabMetadataService = inject(CrmTabMetadataService);
|
||||
|
||||
private _host = inject(CustomerDetailsViewMainComponent, { host: true });
|
||||
private _store = inject(CustomerSearchStore);
|
||||
private _customerService = inject(CrmCustomerService);
|
||||
@@ -39,7 +55,9 @@ export class DetailsMainViewDeliveryAddressesComponent
|
||||
|
||||
shippingAddresses$ = this.select((state) => state.shippingAddresses);
|
||||
|
||||
selectedShippingAddress$ = this.select((state) => state.selectedShippingAddress);
|
||||
selectedShippingAddress$ = this.select(
|
||||
(state) => state.selectedShippingAddress,
|
||||
);
|
||||
|
||||
get selectedShippingAddress() {
|
||||
return this.get((s) => s.selectedShippingAddress);
|
||||
@@ -51,7 +69,12 @@ export class DetailsMainViewDeliveryAddressesComponent
|
||||
this._store.isBusinessKonto$,
|
||||
this._store.isMitarbeiter$,
|
||||
this._store.isKundenkarte$,
|
||||
]).pipe(map(([isBusinessKonto, isMitarbeiter, isKundenkarte]) => isBusinessKonto || isMitarbeiter || isKundenkarte));
|
||||
]).pipe(
|
||||
map(
|
||||
([isBusinessKonto, isMitarbeiter, isKundenkarte]) =>
|
||||
isBusinessKonto || isMitarbeiter || isKundenkarte,
|
||||
),
|
||||
);
|
||||
|
||||
get showCustomerAddress() {
|
||||
return this._store.isBusinessKonto || this._store.isMitarbeiter;
|
||||
@@ -67,13 +90,29 @@ export class DetailsMainViewDeliveryAddressesComponent
|
||||
this._store.isMitarbeiter$,
|
||||
]).pipe(
|
||||
map(
|
||||
([isOnlinekonto, isOnlineKontoMitKundenkarte, isKundenkarte, isBusinessKonto, isMitarbeiter]) =>
|
||||
isOnlinekonto || isOnlineKontoMitKundenkarte || isKundenkarte || isBusinessKonto || isMitarbeiter,
|
||||
([
|
||||
isOnlinekonto,
|
||||
isOnlineKontoMitKundenkarte,
|
||||
isKundenkarte,
|
||||
isBusinessKonto,
|
||||
isMitarbeiter,
|
||||
]) =>
|
||||
isOnlinekonto ||
|
||||
isOnlineKontoMitKundenkarte ||
|
||||
isKundenkarte ||
|
||||
isBusinessKonto ||
|
||||
isMitarbeiter,
|
||||
),
|
||||
);
|
||||
|
||||
editRoute$ = combineLatest([this._store.processId$, this._store.customerId$, this._store.isBusinessKonto$]).pipe(
|
||||
map(([processId, customerId, isB2b]) => this._navigation.editRoute({ processId, customerId, isB2b })),
|
||||
editRoute$ = combineLatest([
|
||||
this._store.processId$,
|
||||
this._store.customerId$,
|
||||
this._store.isBusinessKonto$,
|
||||
]).pipe(
|
||||
map(([processId, customerId, isB2b]) =>
|
||||
this._navigation.editRoute({ processId, customerId, isB2b }),
|
||||
),
|
||||
);
|
||||
|
||||
addShippingAddressRoute$ = combineLatest([
|
||||
@@ -82,15 +121,25 @@ export class DetailsMainViewDeliveryAddressesComponent
|
||||
this._store.customerId$,
|
||||
]).pipe(
|
||||
map(([canAddNewAddress, processId, customerId]) =>
|
||||
canAddNewAddress ? this._navigation.addShippingAddressRoute({ processId, customerId }) : undefined,
|
||||
canAddNewAddress
|
||||
? this._navigation.addShippingAddressRoute({ processId, customerId })
|
||||
: undefined,
|
||||
),
|
||||
);
|
||||
|
||||
editShippingAddressRoute$ = (shippingAddressId: number) =>
|
||||
combineLatest([this.canEditAddress$, this._store.processId$, this._store.customerId$]).pipe(
|
||||
combineLatest([
|
||||
this.canEditAddress$,
|
||||
this._store.processId$,
|
||||
this._store.customerId$,
|
||||
]).pipe(
|
||||
map(([canEditAddress, processId, customerId]) => {
|
||||
if (canEditAddress) {
|
||||
return this._navigation.editShippingAddressRoute({ processId, customerId, shippingAddressId });
|
||||
return this._navigation.editShippingAddressRoute({
|
||||
processId,
|
||||
customerId,
|
||||
shippingAddressId,
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
}),
|
||||
@@ -100,7 +149,12 @@ export class DetailsMainViewDeliveryAddressesComponent
|
||||
this._store.isKundenkarte$,
|
||||
this._store.isBusinessKonto$,
|
||||
this._store.isMitarbeiter$,
|
||||
]).pipe(map(([isKundenkarte, isBusinessKonto, isMitarbeiter]) => isKundenkarte || isBusinessKonto || isMitarbeiter));
|
||||
]).pipe(
|
||||
map(
|
||||
([isKundenkarte, isBusinessKonto, isMitarbeiter]) =>
|
||||
isKundenkarte || isBusinessKonto || isMitarbeiter,
|
||||
),
|
||||
);
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -110,20 +164,32 @@ export class DetailsMainViewDeliveryAddressesComponent
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._store.customerId$.pipe(takeUntil(this._onDestroy$)).subscribe((customerId) => {
|
||||
this.resetStore();
|
||||
if (customerId) {
|
||||
this.loadShippingAddresses(customerId);
|
||||
}
|
||||
});
|
||||
this._store.customerId$
|
||||
.pipe(takeUntil(this._onDestroy$))
|
||||
.subscribe((customerId) => {
|
||||
this.resetStore();
|
||||
if (customerId) {
|
||||
this.loadShippingAddresses(customerId);
|
||||
}
|
||||
});
|
||||
|
||||
combineLatest([this.selectedShippingAddress$, this._store.customer$])
|
||||
.pipe(takeUntil(this._onDestroy$))
|
||||
.subscribe(([selectedShippingAddress, customer]) => {
|
||||
if (selectedShippingAddress) {
|
||||
this._host.setShippingAddress(this._createShippingAddressFromShippingAddress(selectedShippingAddress));
|
||||
this._host.setShippingAddress(
|
||||
this._createShippingAddressFromShippingAddress(
|
||||
selectedShippingAddress,
|
||||
),
|
||||
);
|
||||
this.crmTabMetadataService.setSelectedShippingAddressId(
|
||||
this.tabId(),
|
||||
selectedShippingAddress?.id,
|
||||
);
|
||||
} else if (this.showCustomerAddress) {
|
||||
this._host.setShippingAddress(this._createShippingAddressFromCustomer(customer));
|
||||
this._host.setShippingAddress(
|
||||
this._createShippingAddressFromCustomer(customer),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -140,8 +206,12 @@ export class DetailsMainViewDeliveryAddressesComponent
|
||||
title: customer?.title,
|
||||
firstName: customer?.firstName,
|
||||
lastName: customer?.lastName,
|
||||
communicationDetails: customer?.communicationDetails ? { ...customer?.communicationDetails } : undefined,
|
||||
organisation: customer?.organisation ? { ...customer?.organisation } : undefined,
|
||||
communicationDetails: customer?.communicationDetails
|
||||
? { ...customer?.communicationDetails }
|
||||
: undefined,
|
||||
organisation: customer?.organisation
|
||||
? { ...customer?.organisation }
|
||||
: undefined,
|
||||
address: customer?.address ? { ...customer?.address } : undefined,
|
||||
};
|
||||
}
|
||||
@@ -153,8 +223,12 @@ export class DetailsMainViewDeliveryAddressesComponent
|
||||
title: address.title,
|
||||
firstName: address.firstName,
|
||||
lastName: address.lastName,
|
||||
communicationDetails: address.communicationDetails ? { ...address.communicationDetails } : undefined,
|
||||
organisation: address.organisation ? { ...address.organisation } : undefined,
|
||||
communicationDetails: address.communicationDetails
|
||||
? { ...address.communicationDetails }
|
||||
: undefined,
|
||||
organisation: address.organisation
|
||||
? { ...address.organisation }
|
||||
: undefined,
|
||||
address: address.address ? { ...address.address } : undefined,
|
||||
source: address.id,
|
||||
};
|
||||
@@ -165,26 +239,36 @@ export class DetailsMainViewDeliveryAddressesComponent
|
||||
switchMap((customerId) =>
|
||||
this._customerService
|
||||
.getShippingAddresses({ customerId })
|
||||
.pipe(tapResponse(this.handleLoadShippingAddressesResponse, this.handleLoadAssignedPayersError)),
|
||||
.pipe(
|
||||
tapResponse(
|
||||
this.handleLoadShippingAddressesResponse,
|
||||
this.handleLoadAssignedPayersError,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
handleLoadShippingAddressesResponse = (response: ListResponseArgsOfAssignedPayerDTO) => {
|
||||
const selectedShippingAddress = response.result.reduce<ShippingAddressDTO>((prev, curr) => {
|
||||
if (!this.showCustomerAddress && !prev) {
|
||||
handleLoadShippingAddressesResponse = (
|
||||
response: ListResponseArgsOfAssignedPayerDTO,
|
||||
) => {
|
||||
const selectedShippingAddress = response.result.reduce<ShippingAddressDTO>(
|
||||
(prev, curr) => {
|
||||
if (!this.showCustomerAddress && !prev) {
|
||||
return curr;
|
||||
}
|
||||
|
||||
const prevDate = new Date(prev?.isDefault ?? 0);
|
||||
const currDate = new Date(curr?.isDefault ?? 0);
|
||||
|
||||
if (prevDate > currDate) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
const prevDate = new Date(prev?.isDefault ?? 0);
|
||||
const currDate = new Date(curr?.isDefault ?? 0);
|
||||
|
||||
if (prevDate > currDate) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
return curr;
|
||||
}, undefined);
|
||||
},
|
||||
undefined,
|
||||
);
|
||||
|
||||
this.patchState({
|
||||
shippingAddresses: response.result,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
export const SELECTED_BRANCH_METADATA_KEY = 'CHECKOUT_SELECTED_BRANCH_ID';
|
||||
export const SELECTED_BRANCH_METADATA_KEY =
|
||||
'checkout-data-access.checkoutSelectedBranchId';
|
||||
|
||||
export const CHECKOUT_SHOPPING_CART_ID_METADATA_KEY =
|
||||
'CHECKOUT_SHOPPING_CART_ID';
|
||||
'checkout-data-access.checkoutShoppingCartId';
|
||||
|
||||
export const CHECKOUT_REWARD_SHOPPING_CART_ID_METADATA_KEY =
|
||||
'CHECKOUT_REWARD_SHOPPING_CART_ID';
|
||||
'checkout-data-access.checkoutRewardShoppingCartId';
|
||||
|
||||
@@ -5,6 +5,10 @@ import { ShoppingCartService } from '../services';
|
||||
export class ShoppingCartFacade {
|
||||
#shoppingCartService = inject(ShoppingCartService);
|
||||
|
||||
createShoppingCart() {
|
||||
return this.#shoppingCartService.createShoppingCart();
|
||||
}
|
||||
|
||||
getShoppingCart(shoppingCartId: number, abortSignal?: AbortSignal) {
|
||||
return this.#shoppingCartService.getShoppingCart(
|
||||
shoppingCartId,
|
||||
|
||||
@@ -43,7 +43,7 @@ const CanAddOLAAvailabilitySchema = z.object({
|
||||
|
||||
const CanAddItemPayloadSchema = z.object({
|
||||
availabilities: z.array(CanAddOLAAvailabilitySchema),
|
||||
customerFeatures: z.record(z.string()),
|
||||
customerFeatures: z.record(z.string().optional()),
|
||||
orderType: OrderTypeSchema,
|
||||
id: z.string(),
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ export class CheckoutMetadataService {
|
||||
tabId,
|
||||
SELECTED_BRANCH_METADATA_KEY,
|
||||
z.number().optional(),
|
||||
this.#tabService.entities(),
|
||||
this.#tabService.entityMap(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -37,13 +37,13 @@ export class CheckoutMetadataService {
|
||||
tabId,
|
||||
CHECKOUT_SHOPPING_CART_ID_METADATA_KEY,
|
||||
z.number().optional(),
|
||||
this.#tabService.entities(),
|
||||
this.#tabService.entityMap(),
|
||||
);
|
||||
}
|
||||
|
||||
setRewardShoppingCartId(tabId: number, shoppingCartId: number | undefined) {
|
||||
this.#tabService.patchTabMetadata(tabId, {
|
||||
CHECKOUT_REWARD_SHOPPING_CART_ID_METADATA_KEY: shoppingCartId,
|
||||
[CHECKOUT_REWARD_SHOPPING_CART_ID_METADATA_KEY]: shoppingCartId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export class CheckoutMetadataService {
|
||||
tabId,
|
||||
CHECKOUT_REWARD_SHOPPING_CART_ID_METADATA_KEY,
|
||||
z.number().optional(),
|
||||
this.#tabService.entities(),
|
||||
this.#tabService.entityMap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
export const getRouteToCustomer = (tabId: number | null) => {
|
||||
const path = [
|
||||
'/kunde',
|
||||
tabId,
|
||||
'customer',
|
||||
{ outlets: { primary: 'search', side: 'search-customer-main' } },
|
||||
].filter(Boolean);
|
||||
|
||||
const queryParams = {
|
||||
filter_customertype: 'webshop&loyalty;loyalty&!webshop', // Filter only Customer Card Customers
|
||||
};
|
||||
|
||||
return { path, queryParams };
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './get-route-to-customer.helper';
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './reward-catalog.resource';
|
||||
export * from './reward-customer-card.resource';
|
||||
export * from './reward-shopping-cart.resource';
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import { inject, resource } from '@angular/core';
|
||||
import { ResponseArgsError } from '@isa/common/data-access';
|
||||
import { injectTabId } from '@isa/core/tabs';
|
||||
import { CustomerCardsFacade } from '@isa/crm/data-access';
|
||||
import { SelectedCustomerFacade } from '@isa/crm/data-access';
|
||||
|
||||
export const createRewardCustomerCardResource = (
|
||||
params: () => {
|
||||
customerId: number | undefined;
|
||||
},
|
||||
) => {
|
||||
export const createRewardCustomerCardResource = () => {
|
||||
const tabId = injectTabId();
|
||||
const customerCardsFacade = inject(CustomerCardsFacade);
|
||||
const selectedCustomerFacade = inject(SelectedCustomerFacade);
|
||||
return resource({
|
||||
params,
|
||||
loader: async ({ abortSignal, params }) => {
|
||||
const { customerId } = params;
|
||||
loader: async ({ abortSignal }) => {
|
||||
const customerId = selectedCustomerFacade.get(tabId()!);
|
||||
|
||||
if (!customerId) {
|
||||
return undefined;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { inject, resource } from '@angular/core';
|
||||
import {
|
||||
CheckoutMetadataService,
|
||||
ShoppingCartFacade,
|
||||
} from '@isa/checkout/data-access';
|
||||
import { injectTabId } from '@isa/core/tabs';
|
||||
|
||||
export const createRewardShoppingCartResource = () => {
|
||||
const tabId = injectTabId();
|
||||
const checkoutMetadataService = inject(CheckoutMetadataService);
|
||||
const shoppingCartFacade = inject(ShoppingCartFacade);
|
||||
return resource({
|
||||
loader: async ({ abortSignal }) => {
|
||||
const shoppingCartId = checkoutMetadataService.getRewardShoppingCartId(
|
||||
tabId()!,
|
||||
);
|
||||
|
||||
if (!shoppingCartId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const fetchCustomerCardsResponse =
|
||||
await shoppingCartFacade.getShoppingCart(shoppingCartId, abortSignal);
|
||||
|
||||
return fetchCustomerCardsResponse;
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply flex flex-col self-end fixed bottom-6;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<button
|
||||
class="fixed right-6 bottom-6"
|
||||
data-which="start-remission"
|
||||
data-what="start-remission"
|
||||
data-which="select-rewards"
|
||||
data-what="select-rewards"
|
||||
uiButton
|
||||
color="brand"
|
||||
size="large"
|
||||
|
||||
@@ -4,8 +4,18 @@ import {
|
||||
linkedSignal,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { RewardCatalogStore } from '@isa/checkout/data-access';
|
||||
import {
|
||||
RewardCatalogStore,
|
||||
CheckoutMetadataService,
|
||||
ShoppingCartFacade,
|
||||
} from '@isa/checkout/data-access';
|
||||
import { injectTabId } from '@isa/core/tabs';
|
||||
import { ButtonComponent } from '@isa/ui/buttons';
|
||||
import { PurchaseOptionsModalService } from '@modal/purchase-options';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { createRewardCustomerCardResource } from '../resources';
|
||||
import { Router } from '@angular/router';
|
||||
import { getRouteToCustomer } from '../helpers';
|
||||
|
||||
@Component({
|
||||
selector: 'reward-action',
|
||||
@@ -15,7 +25,19 @@ import { ButtonComponent } from '@isa/ui/buttons';
|
||||
imports: [ButtonComponent],
|
||||
})
|
||||
export class RewardActionComponent {
|
||||
#router = inject(Router);
|
||||
#store = inject(RewardCatalogStore);
|
||||
#tabId = injectTabId();
|
||||
|
||||
#purchasingOptionsModal = inject(PurchaseOptionsModalService);
|
||||
#shoppingCartFacade = inject(ShoppingCartFacade);
|
||||
#checkoutMetadataService = inject(CheckoutMetadataService);
|
||||
|
||||
rewardCustomerCardResource = createRewardCustomerCardResource(); // TODO: Refactor and use global resource
|
||||
|
||||
customerCardResponseValue = linkedSignal(() =>
|
||||
this.rewardCustomerCardResource.value(),
|
||||
);
|
||||
|
||||
selectedItems = linkedSignal(() => this.#store.selectedItems());
|
||||
|
||||
@@ -23,7 +45,60 @@ export class RewardActionComponent {
|
||||
return Object.keys(this.selectedItems() || {}).length > 0;
|
||||
});
|
||||
|
||||
continueToPurchasingOptions() {
|
||||
console.log('Kaufoptionen Modal öffnen mit: ', this.selectedItems());
|
||||
async continueToPurchasingOptions() {
|
||||
const tabId = this.#tabId();
|
||||
const items = Object.values(this.selectedItems() || {});
|
||||
|
||||
if (!items?.length || !tabId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let rewardShoppingCartId =
|
||||
this.#checkoutMetadataService.getRewardShoppingCartId(tabId);
|
||||
|
||||
if (!rewardShoppingCartId) {
|
||||
rewardShoppingCartId = await this.#createShoppingCart(tabId);
|
||||
}
|
||||
|
||||
const modalRef = await this.#purchasingOptionsModal.open({
|
||||
type: 'add',
|
||||
tabId,
|
||||
shoppingCartId: rewardShoppingCartId,
|
||||
items,
|
||||
useRedemptionPoints: true,
|
||||
});
|
||||
|
||||
const result = await firstValueFrom(modalRef.afterClosed$);
|
||||
|
||||
if (!result?.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#store.clearSelectedItems();
|
||||
|
||||
if (result.data !== 'continue-shopping') {
|
||||
await this.#navigation(tabId);
|
||||
}
|
||||
}
|
||||
|
||||
async #navigation(tabId: number) {
|
||||
const hasCustomer = this.customerCardResponseValue();
|
||||
if (hasCustomer) {
|
||||
// TODO: Update Reward Shopping Cart Resource
|
||||
} else {
|
||||
const route = getRouteToCustomer(tabId);
|
||||
await this.#router.navigate(route.path, {
|
||||
queryParams: route.queryParams,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async #createShoppingCart(tabId: number): Promise<number> {
|
||||
const shoppingCart = await this.#shoppingCartFacade.createShoppingCart();
|
||||
this.#checkoutMetadataService.setRewardShoppingCartId(
|
||||
tabId,
|
||||
shoppingCart.id,
|
||||
);
|
||||
return shoppingCart.id!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply relative;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { RewardHeaderComponent } from './reward-header/reward-header.component';
|
||||
import { RewardListComponent } from './reward-list/reward-list.component';
|
||||
import { injectRestoreScrollPosition } from '@isa/utils/scroll-position';
|
||||
import { RewardActionComponent } from './reward-action/reward-action.component';
|
||||
import { TabService } from '@isa/core/tabs';
|
||||
|
||||
/**
|
||||
* Factory function to retrieve query settings from the activated route data.
|
||||
@@ -49,14 +50,28 @@ function querySettingsFactory() {
|
||||
},
|
||||
})
|
||||
export class RewardCatalogComponent {
|
||||
#tabService = inject(TabService);
|
||||
|
||||
restoreScrollPosition = injectRestoreScrollPosition();
|
||||
|
||||
searchTrigger = signal<SearchTrigger | 'reload' | 'initial'>('initial');
|
||||
|
||||
#filterService = inject(FilterService);
|
||||
|
||||
constructor() {
|
||||
this.initRewardContext();
|
||||
}
|
||||
|
||||
search(trigger: SearchTrigger): void {
|
||||
this.searchTrigger.set(trigger); // Ist entweder 'scan', 'input', 'filter' oder 'orderBy'
|
||||
this.#filterService.commit();
|
||||
}
|
||||
|
||||
// Wichtig damit Kundensuche weiß, dass wir im Reward Kontext sind - Über Header CTA oder Kaufoptionen gelangt man zur Kundensuche
|
||||
initRewardContext() {
|
||||
const tabId = this.#tabService.activatedTabId();
|
||||
if (tabId) {
|
||||
this.#tabService.patchTabMetadata(tabId, { context: 'reward' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
>{{ card()?.firstName }} {{ card()?.lastName }}
|
||||
</span>
|
||||
<span class="isa-text-body-1-bold"
|
||||
>{{ card()?.totalPoints ?? 0 }} Lesepunkte</span
|
||||
>{{ customerCardTotalPoints() }} Lesepunkte</span
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
type="button"
|
||||
color="subtle"
|
||||
size="small"
|
||||
(click)="resetCustomer()"
|
||||
(click)="reset.emit()"
|
||||
>
|
||||
Zurücksetzen
|
||||
</ui-text-button>
|
||||
@@ -21,5 +21,23 @@
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<span class="isa-text-body-1-regular">Prämien ausgewählt</span>
|
||||
<span class="isa-text-body-1-bold">0</span>
|
||||
<span
|
||||
*uiSkeletonLoader="shoppingCartResponseFetching()"
|
||||
class="isa-text-body-1-bold"
|
||||
>{{ cartItemsLength() }}</span
|
||||
>
|
||||
</div>
|
||||
|
||||
@if (cartItemsLength()) {
|
||||
<button
|
||||
data-which="continue-to-reward-checkout"
|
||||
data-what="continue-to-reward-checkout"
|
||||
uiButton
|
||||
color="brand"
|
||||
size="large"
|
||||
[disabled]="disableContinueCta()"
|
||||
(click)="continueToRewardCheckout()"
|
||||
>
|
||||
Prämienausgabe
|
||||
</button>
|
||||
}
|
||||
|
||||
@@ -1,27 +1,60 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
inject,
|
||||
input,
|
||||
linkedSignal,
|
||||
output,
|
||||
} from '@angular/core';
|
||||
import { TextButtonComponent } from '@isa/ui/buttons';
|
||||
import { BonusCardInfo, SelectedCustomerFacade } from '@isa/crm/data-access';
|
||||
import { injectTabId } from '@isa/core/tabs';
|
||||
import { ButtonComponent, TextButtonComponent } from '@isa/ui/buttons';
|
||||
import { BonusCardInfo } from '@isa/crm/data-access';
|
||||
import { createRewardShoppingCartResource } from '../../resources';
|
||||
import { SkeletonLoaderDirective } from '@isa/ui/skeleton-loader';
|
||||
|
||||
@Component({
|
||||
selector: 'reward-customer-card',
|
||||
templateUrl: './reward-customer-card.component.html',
|
||||
styleUrl: './reward-customer-card.component.css',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [TextButtonComponent],
|
||||
imports: [TextButtonComponent, ButtonComponent, SkeletonLoaderDirective],
|
||||
})
|
||||
export class RewardCustomerCardComponent {
|
||||
tabId = injectTabId();
|
||||
selectedCustomerFacade = inject(SelectedCustomerFacade);
|
||||
|
||||
card = input.required<BonusCardInfo>();
|
||||
reset = output<void>();
|
||||
|
||||
resetCustomer() {
|
||||
this.selectedCustomerFacade.clear(this.tabId()!);
|
||||
rewardShoppingCartResource = createRewardShoppingCartResource(); // TODO: Refactor and use global resource
|
||||
|
||||
shoppingCartResponseValue = linkedSignal(() =>
|
||||
this.rewardShoppingCartResource.value(),
|
||||
);
|
||||
|
||||
shoppingCartResponseFetching = linkedSignal(
|
||||
() => this.rewardShoppingCartResource.status() === 'loading',
|
||||
);
|
||||
|
||||
cartItemsLength = linkedSignal(
|
||||
() => this.shoppingCartResponseValue()?.items?.length ?? 0,
|
||||
);
|
||||
|
||||
cartTotalLoyaltyPoints = linkedSignal(() => {
|
||||
return (
|
||||
this.shoppingCartResponseValue()?.items?.reduce(
|
||||
(sum, item) => sum + (item?.data?.loyalty?.value ?? 0),
|
||||
0,
|
||||
) ?? 0
|
||||
);
|
||||
});
|
||||
|
||||
customerCardTotalPoints = linkedSignal(() => this.card()?.totalPoints ?? 0);
|
||||
|
||||
disableContinueCta = linkedSignal(() => {
|
||||
return (
|
||||
this.shoppingCartResponseFetching() ||
|
||||
this.customerCardTotalPoints() <= 0 ||
|
||||
this.cartTotalLoyaltyPoints() > this.customerCardTotalPoints()
|
||||
);
|
||||
});
|
||||
|
||||
continueToRewardCheckout() {
|
||||
// TODO: Navigate to Reward Checkout Page
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
@let cardFetching = customerCardResponseFetching();
|
||||
@if (!cardFetching) {
|
||||
@if (card) {
|
||||
<reward-customer-card [card]="card"></reward-customer-card>
|
||||
<reward-customer-card
|
||||
[card]="card"
|
||||
(reset)="resetCustomer()"
|
||||
></reward-customer-card>
|
||||
} @else {
|
||||
<reward-start-card></reward-start-card>
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ import {
|
||||
linkedSignal,
|
||||
} from '@angular/core';
|
||||
import { RewardStartCardComponent } from './reward-start-card/reward-start-card.component';
|
||||
import { injectTabId } from '@isa/core/tabs';
|
||||
import { SelectedCustomerFacade } from '@isa/crm/data-access';
|
||||
import { RewardCustomerCardComponent } from './reward-customer-card/reward-customer-card.component';
|
||||
import { createRewardCustomerCardResource } from '../resources';
|
||||
import { IconButtonComponent } from '@isa/ui/buttons';
|
||||
import { injectTabId } from '@isa/core/tabs';
|
||||
import { SelectedCustomerFacade } from '@isa/crm/data-access';
|
||||
@Component({
|
||||
selector: 'reward-header',
|
||||
templateUrl: './reward-header.component.html',
|
||||
@@ -25,25 +25,15 @@ export class RewardHeaderComponent {
|
||||
tabId = injectTabId();
|
||||
selectedCustomerFacade = inject(SelectedCustomerFacade);
|
||||
|
||||
selectedCustomerId = linkedSignal(() =>
|
||||
this.selectedCustomerFacade.get(this.tabId()!),
|
||||
);
|
||||
rewardCustomerCardResource = createRewardCustomerCardResource(); // TODO: Refactor and use global resource
|
||||
|
||||
rewardCustomerCardResource = createRewardCustomerCardResource(() => {
|
||||
return {
|
||||
customerId: this.selectedCustomerId(),
|
||||
};
|
||||
});
|
||||
readonly customerCardResponseValue =
|
||||
this.rewardCustomerCardResource.value.asReadonly();
|
||||
readonly customerCardResponseFetching =
|
||||
this.rewardCustomerCardResource.isLoading;
|
||||
|
||||
// TODO: Falls notwendig und die Karte allein nicht ausreicht, Customer Fetchen und überprüfen ob folgende features vorhanden sind:
|
||||
// features?.some((c) => c.key === 'p4mUser')
|
||||
// oder
|
||||
// features?.some((c) => c.key === 'd-account' && c.description.includes('Kundenkarte') && c.enabled)
|
||||
customerCardResponseValue = linkedSignal(() =>
|
||||
this.rewardCustomerCardResource.value(),
|
||||
);
|
||||
|
||||
customerCardResponseFetching = linkedSignal(
|
||||
() => this.rewardCustomerCardResource.status() === 'loading',
|
||||
);
|
||||
resetCustomer() {
|
||||
this.selectedCustomerFacade.clear(this.tabId()!);
|
||||
this.rewardCustomerCardResource.reload();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
uiButton
|
||||
color="tertiary"
|
||||
size="large"
|
||||
[routerLink]="pathToCrmSearch()"
|
||||
[queryParams]="queryParams"
|
||||
(click)="setTabContext()"
|
||||
[routerLink]="route().path"
|
||||
[queryParams]="route().queryParams"
|
||||
>
|
||||
Kund*in auswählen
|
||||
</a>
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
linkedSignal,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, computed } from '@angular/core';
|
||||
import { ButtonComponent } from '@isa/ui/buttons';
|
||||
import { injectTabId, TabService } from '@isa/core/tabs';
|
||||
import { injectTabId } from '@isa/core/tabs';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { getRouteToCustomer } from '../../helpers';
|
||||
@Component({
|
||||
selector: 'reward-start-card',
|
||||
templateUrl: './reward-start-card.component.html',
|
||||
@@ -15,24 +11,6 @@ import { RouterLink } from '@angular/router';
|
||||
imports: [ButtonComponent, RouterLink],
|
||||
})
|
||||
export class RewardStartCardComponent {
|
||||
tabService = inject(TabService);
|
||||
tabId = injectTabId();
|
||||
|
||||
pathToCrmSearch = linkedSignal(() =>
|
||||
[
|
||||
'/kunde',
|
||||
this.tabId(),
|
||||
'customer',
|
||||
{ outlets: { primary: 'search', side: 'search-customer-main' } },
|
||||
].filter(Boolean),
|
||||
);
|
||||
|
||||
queryParams = {
|
||||
filter_customertype: 'webshop&loyalty;loyalty&!webshop', // Filter only Customer Card Customers
|
||||
};
|
||||
|
||||
// Wichtig damit Kundensuche weiß, dass wir im Reward Kontext sind
|
||||
setTabContext() {
|
||||
this.tabService.patchTabMetadata(this.tabId()!, { context: 'reward' });
|
||||
}
|
||||
route = computed(() => getRouteToCustomer(this.tabId()));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import z from 'zod';
|
||||
import { Tab } from './schemas';
|
||||
import { computed } from '@angular/core';
|
||||
|
||||
export function getTabHelper(
|
||||
tabId: number,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './lib/facades';
|
||||
export * from './lib/constants';
|
||||
export * from './lib/models';
|
||||
export * from './lib/services';
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
export const SELECTED_CUSTOMER_ID = 'crm-data-access.selectedCustomerId';
|
||||
export const SELECTED_SHIPPING_ADDRESS_ID =
|
||||
'crm-data-access.selectedShippingAddressId';
|
||||
export const SELECTED_PAYER_ADDRESS_ID =
|
||||
'crm-data-access.selectedPayerAddressId';
|
||||
|
||||
@@ -9,7 +9,7 @@ export class CustomerFacade {
|
||||
|
||||
async fetchCustomer(
|
||||
params: FetchCustomerInput,
|
||||
abortSignal?: AbortSignal,
|
||||
abortSignal: AbortSignal,
|
||||
): Promise<Customer | undefined> {
|
||||
const res = await this.#customerService.fetchCustomer(params, abortSignal);
|
||||
return res.result;
|
||||
|
||||
@@ -9,11 +9,11 @@ export class SelectedCustomerFacade {
|
||||
this.#crmTabMetadataService.setSelectedCustomerId(tabId, customerId);
|
||||
}
|
||||
|
||||
get(tab: number) {
|
||||
return this.#crmTabMetadataService.selectedCustomerId(tab);
|
||||
get(tabId: number) {
|
||||
return this.#crmTabMetadataService.selectedCustomerId(tabId);
|
||||
}
|
||||
|
||||
clear(tab: number) {
|
||||
this.#crmTabMetadataService.setSelectedCustomerId(tab, undefined);
|
||||
clear(tabId: number) {
|
||||
this.#crmTabMetadataService.setSelectedCustomerId(tabId, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { TabService, getMetadataHelper, getTabHelper } from '@isa/core/tabs';
|
||||
import { SELECTED_CUSTOMER_ID } from '../constants';
|
||||
import { TabService, getMetadataHelper } from '@isa/core/tabs';
|
||||
import {
|
||||
SELECTED_CUSTOMER_ID,
|
||||
SELECTED_PAYER_ADDRESS_ID,
|
||||
SELECTED_SHIPPING_ADDRESS_ID,
|
||||
} from '../constants';
|
||||
import z from 'zod';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
@@ -12,7 +16,25 @@ export class CrmTabMetadataService {
|
||||
tabId,
|
||||
SELECTED_CUSTOMER_ID,
|
||||
z.number().optional(),
|
||||
this.#tabService.entities(),
|
||||
this.#tabService.entityMap(),
|
||||
);
|
||||
}
|
||||
|
||||
selectedShippingAddressId(tabId: number): number | undefined {
|
||||
return getMetadataHelper(
|
||||
tabId,
|
||||
SELECTED_SHIPPING_ADDRESS_ID,
|
||||
z.number().optional(),
|
||||
this.#tabService.entityMap(),
|
||||
);
|
||||
}
|
||||
|
||||
selectedPayerAddressId(tabId: number): number | undefined {
|
||||
return getMetadataHelper(
|
||||
tabId,
|
||||
SELECTED_PAYER_ADDRESS_ID,
|
||||
z.number().optional(),
|
||||
this.#tabService.entityMap(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,4 +43,19 @@ export class CrmTabMetadataService {
|
||||
[SELECTED_CUSTOMER_ID]: customerId,
|
||||
});
|
||||
}
|
||||
|
||||
setSelectedShippingAddressId(
|
||||
tabId: number,
|
||||
shippingAddressId: number | undefined,
|
||||
) {
|
||||
this.#tabService.patchTabMetadata(tabId, {
|
||||
[SELECTED_SHIPPING_ADDRESS_ID]: shippingAddressId,
|
||||
});
|
||||
}
|
||||
|
||||
setSelectedPayerAddressId(tabId: number, payerAddressId: number | undefined) {
|
||||
this.#tabService.patchTabMetadata(tabId, {
|
||||
[SELECTED_PAYER_ADDRESS_ID]: payerAddressId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user