mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merged PR 1974: feat(crm): introduce PrimaryCustomerCardResource and format-name utility
feat(crm): introduce PrimaryCustomerCardResource and format-name utility Replace SelectedCustomerBonusCardsResource with a new PrimaryCustomerCardResource that automatically loads and exposes the primary customer card as a signal. This simplifies customer card access across the application by providing a centralized, root-level injectable resource with automatic tab synchronization. Create new @isa/utils/format-name library to consolidate customer name formatting logic previously duplicated across components. The utility formats names with configurable first name, last name, and organization name fields. Key changes: - Add PrimaryCustomerCardResource as providedIn root service with automatic customer selection tracking via effect - Remove SelectedCustomerBonusCardsResource and its manual provisioning - Extract formatName function to dedicated utility library with Vitest setup - Update all reward-related components to use new resource pattern - Migrate OMS components to use centralized format-name utility - Add comprehensive unit tests for formatName function BREAKING CHANGE: SelectedCustomerBonusCardsResource has been removed Ref: #5389
This commit is contained in:
committed by
Lorenz Hilpert
parent
f549c59bc8
commit
0b76552211
@@ -1,49 +0,0 @@
|
||||
import { effect, inject, Injectable, resource, signal } from '@angular/core';
|
||||
import { CrmSearchService, CrmTabMetadataService } from '../services';
|
||||
import { TabService } from '@isa/core/tabs';
|
||||
|
||||
@Injectable()
|
||||
export class CustomerBonusCardsResource {
|
||||
#customerService = inject(CrmSearchService);
|
||||
|
||||
#params = signal<{ customerId: number | undefined }>({
|
||||
customerId: undefined,
|
||||
});
|
||||
|
||||
params(params: { customerId?: number }) {
|
||||
this.#params.update((p) => ({ ...p, ...params }));
|
||||
}
|
||||
|
||||
readonly resource = resource({
|
||||
params: () => this.#params(),
|
||||
loader: async ({ params, abortSignal }) => {
|
||||
if (!params.customerId) {
|
||||
return undefined;
|
||||
}
|
||||
const res = await this.#customerService.fetchCustomerCards(
|
||||
{
|
||||
customerId: params.customerId,
|
||||
},
|
||||
abortSignal,
|
||||
);
|
||||
return res.result;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class SelectedCustomerBonusCardsResource extends CustomerBonusCardsResource {
|
||||
#tabId = inject(TabService).activatedTabId;
|
||||
#customerMetadata = inject(CrmTabMetadataService);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
effect(() => {
|
||||
const tabId = this.#tabId();
|
||||
const customerId = tabId
|
||||
? this.#customerMetadata.selectedCustomerId(tabId)
|
||||
: undefined;
|
||||
this.params({ customerId });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
export * from './country.resource';
|
||||
export * from './customer-bonus-cards.resource';
|
||||
export * from './primary-customer-card.resource';
|
||||
export * from './customer-shipping-address.resource';
|
||||
export * from './customer-shipping-addresses.resource';
|
||||
export * from './customer.resource';
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import {
|
||||
computed,
|
||||
effect,
|
||||
inject,
|
||||
Injectable,
|
||||
resource,
|
||||
signal,
|
||||
untracked,
|
||||
} from '@angular/core';
|
||||
import { CrmSearchService, CrmTabMetadataService } from '../services';
|
||||
import { injectTabId } from '@isa/core/tabs';
|
||||
import { BonusCardInfo } from '../models';
|
||||
import { getPrimaryBonusCard } from '../helpers';
|
||||
|
||||
/**
|
||||
* Resource for the primary customer card of a customer.
|
||||
*
|
||||
* This resource automatically loads the primary bonus card of a customer
|
||||
* as soon as a `customerId` is set in the tab metadata. An internal effect
|
||||
* monitors changes to the `selectedCustomerId` in the active tab and
|
||||
* triggers loading of the customer card automatically.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const resource = inject(PrimaryCustomerCardResource);
|
||||
*
|
||||
* // Primary customer card (automatically loaded via effect)
|
||||
* const card = resource.primaryCustomerCard();
|
||||
*
|
||||
* // Loading status
|
||||
* const isLoading = resource.loading();
|
||||
* ```
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PrimaryCustomerCardResource {
|
||||
#customerMetadata = inject(CrmTabMetadataService);
|
||||
#customerService = inject(CrmSearchService);
|
||||
#tabId = injectTabId();
|
||||
|
||||
#customerId = signal<number | undefined>(undefined);
|
||||
|
||||
#primaryCustomerCardResource = resource({
|
||||
params: computed(() => ({ customerId: this.#customerId() })),
|
||||
loader: async ({
|
||||
params,
|
||||
abortSignal,
|
||||
}): Promise<BonusCardInfo | undefined> => {
|
||||
const tabId = this.#tabId();
|
||||
if (!tabId || !params.customerId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const res = await this.#customerService.fetchCustomerCards(
|
||||
{ customerId: params.customerId },
|
||||
abortSignal,
|
||||
);
|
||||
|
||||
if (res?.result?.length > 0) {
|
||||
return getPrimaryBonusCard(res.result);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
defaultValue: undefined,
|
||||
});
|
||||
|
||||
/**
|
||||
* Signal containing the primary customer card of the selected customer.
|
||||
* Automatically updated when the `selectedCustomerId` changes in the tab metadata.
|
||||
*/
|
||||
readonly primaryCustomerCard =
|
||||
this.#primaryCustomerCardResource.value.asReadonly();
|
||||
|
||||
/**
|
||||
* Signal indicating whether the customer card is currently being loaded.
|
||||
*/
|
||||
readonly loading = this.#primaryCustomerCardResource.isLoading;
|
||||
|
||||
/**
|
||||
* Signal containing an error message if an error occurred during loading.
|
||||
* Returns `null` if no error is present.
|
||||
*/
|
||||
readonly error = computed(
|
||||
() => this.#primaryCustomerCardResource.error()?.message ?? null,
|
||||
);
|
||||
|
||||
/**
|
||||
* Loads the primary customer card for the specified customer ID.
|
||||
*
|
||||
* @param customerId - The ID of the customer whose primary card should be loaded
|
||||
*/
|
||||
loadPrimaryCustomerCard(customerId: number | undefined) {
|
||||
this.#customerId.set(customerId);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
/**
|
||||
* Effect: Monitors changes to the `selectedCustomerId` in the tab metadata
|
||||
* and automatically loads the primary customer card whenever a new customer ID
|
||||
* is set. This ensures that the customer card is always in sync with the
|
||||
* customer selection in the active tab.
|
||||
*
|
||||
* Uses `untracked` to prevent creating additional signal dependencies when
|
||||
* checking if the customer ID has actually changed, avoiding unnecessary reloads.
|
||||
*/
|
||||
effect(() => {
|
||||
const tabId = this.#tabId();
|
||||
let customerId = undefined;
|
||||
|
||||
if (tabId) {
|
||||
customerId = this.#customerMetadata.selectedCustomerId(tabId);
|
||||
}
|
||||
|
||||
untracked(() => {
|
||||
if (this.#customerId() !== customerId) {
|
||||
this.loadPrimaryCustomerCard(customerId);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user