diff --git a/apps/demo/src/app/cart/components/cart/cart.component.spec.ts b/apps/demo/src/app/cart/components/cart/cart.component.spec.ts index 225691d041..192b0a4b13 100644 --- a/apps/demo/src/app/cart/components/cart/cart.component.spec.ts +++ b/apps/demo/src/app/cart/components/cart/cart.component.spec.ts @@ -96,8 +96,8 @@ describe('Cart', () => { fixture = TestBed.createComponent(WrapperComponent); wrapper = fixture.componentInstance; - daffCartFacade.items$ = new BehaviorSubject(cart.items); - daffCartFacade.isCartEmpty$ = new BehaviorSubject(true); + daffCartFacade.cart$.next(cart); + daffCartFacade.isCartEmpty$.next(true); wrapper.cartValue = cart; component = fixture.debugElement.query(By.css('demo-cart')).componentInstance; diff --git a/apps/demo/src/app/cart/components/cart/cart.component.ts b/apps/demo/src/app/cart/components/cart/cart.component.ts index ab522a6c9e..30eba808c7 100644 --- a/apps/demo/src/app/cart/components/cart/cart.component.ts +++ b/apps/demo/src/app/cart/components/cart/cart.component.ts @@ -26,8 +26,8 @@ export class CartComponent implements OnInit { ) {} ngOnInit(): void { - this.itemCount$ = this.facade.items$.pipe( - map(items => items.length), + this.itemCount$ = this.facade.cart$.pipe( + map((cart) => cart?.items.length), ); this.isCartEmpty$ = this.facade.isCartEmpty$; } diff --git a/libs/cart/driver/in-memory/src/drivers/cart-address/cart-address.service.ts b/libs/cart/driver/in-memory/src/drivers/cart-address/cart-address.service.ts index 89fad32efc..6db532b418 100644 --- a/libs/cart/driver/in-memory/src/drivers/cart-address/cart-address.service.ts +++ b/libs/cart/driver/in-memory/src/drivers/cart-address/cart-address.service.ts @@ -14,7 +14,7 @@ import { DaffCartAddressServiceInterface } from '@daffodil/cart/driver'; @Injectable({ providedIn: 'root', }) -export class DaffInMemoryCartAddressService implements DaffCartAddressServiceInterface { +export class DaffInMemoryCartAddressService implements DaffCartAddressServiceInterface { /** * The URL with which the driver makes calls to the backend. */ diff --git a/libs/cart/driver/in-memory/src/drivers/cart-billing-address/cart-billing-address.service.ts b/libs/cart/driver/in-memory/src/drivers/cart-billing-address/cart-billing-address.service.ts index bc65b081d9..21b723f432 100644 --- a/libs/cart/driver/in-memory/src/drivers/cart-billing-address/cart-billing-address.service.ts +++ b/libs/cart/driver/in-memory/src/drivers/cart-billing-address/cart-billing-address.service.ts @@ -14,7 +14,7 @@ import { DaffCartBillingAddressServiceInterface } from '@daffodil/cart/driver'; @Injectable({ providedIn: 'root', }) -export class DaffInMemoryCartBillingAddressService implements DaffCartBillingAddressServiceInterface { +export class DaffInMemoryCartBillingAddressService implements DaffCartBillingAddressServiceInterface { /** * The URL with which the driver makes calls to the backend. */ diff --git a/libs/cart/driver/in-memory/src/drivers/cart-item/cart-item.service.ts b/libs/cart/driver/in-memory/src/drivers/cart-item/cart-item.service.ts index c9f6559ca3..48a15f9712 100644 --- a/libs/cart/driver/in-memory/src/drivers/cart-item/cart-item.service.ts +++ b/libs/cart/driver/in-memory/src/drivers/cart-item/cart-item.service.ts @@ -15,11 +15,7 @@ import { DaffCartItemServiceInterface } from '@daffodil/cart/driver'; @Injectable({ providedIn: 'root', }) -export class DaffInMemoryCartItemService implements DaffCartItemServiceInterface< -DaffCartItem, -DaffCartItemInput, -DaffCart -> { +export class DaffInMemoryCartItemService implements DaffCartItemServiceInterface { /** * The URL with which the driver makes calls to the backend. */ diff --git a/libs/cart/driver/in-memory/src/drivers/cart-shipping-address/cart-shipping-address.service.ts b/libs/cart/driver/in-memory/src/drivers/cart-shipping-address/cart-shipping-address.service.ts index ca978654ac..929b7173cc 100644 --- a/libs/cart/driver/in-memory/src/drivers/cart-shipping-address/cart-shipping-address.service.ts +++ b/libs/cart/driver/in-memory/src/drivers/cart-shipping-address/cart-shipping-address.service.ts @@ -14,7 +14,7 @@ import { DaffCartShippingAddressServiceInterface } from '@daffodil/cart/driver'; @Injectable({ providedIn: 'root', }) -export class DaffInMemoryCartShippingAddressService implements DaffCartShippingAddressServiceInterface { +export class DaffInMemoryCartShippingAddressService implements DaffCartShippingAddressServiceInterface { /** * The URL with which the driver makes calls to the backend. */ diff --git a/libs/cart/driver/in-memory/src/drivers/cart-shipping-information/cart-shipping-information.service.ts b/libs/cart/driver/in-memory/src/drivers/cart-shipping-information/cart-shipping-information.service.ts index 0faa2ab122..9be63e08a9 100644 --- a/libs/cart/driver/in-memory/src/drivers/cart-shipping-information/cart-shipping-information.service.ts +++ b/libs/cart/driver/in-memory/src/drivers/cart-shipping-information/cart-shipping-information.service.ts @@ -14,10 +14,7 @@ import { DaffCartShippingInformationServiceInterface } from '@daffodil/cart/driv @Injectable({ providedIn: 'root', }) -export class DaffInMemoryCartShippingInformationService implements DaffCartShippingInformationServiceInterface< -DaffCartShippingRate, -DaffCart -> { +export class DaffInMemoryCartShippingInformationService implements DaffCartShippingInformationServiceInterface { /** * The URL with which the driver makes calls to the backend. */ diff --git a/libs/cart/driver/magento/src/cart.service.ts b/libs/cart/driver/magento/src/cart.service.ts index c1665bfa09..b053aa9ac9 100644 --- a/libs/cart/driver/magento/src/cart.service.ts +++ b/libs/cart/driver/magento/src/cart.service.ts @@ -63,11 +63,7 @@ export class DaffMagentoCartService implements DaffCartServiceInterface, + @Inject(DaffCartItemDriver) private cartItemDriver: DaffCartItemServiceInterface, @Inject(DAFF_CART_MAGENTO_EXTRA_CART_FRAGMENTS) private extraCartFragments: DocumentNode[], ) {} diff --git a/libs/cart/driver/src/interfaces/cart-address-service.interface.ts b/libs/cart/driver/src/interfaces/cart-address-service.interface.ts index 8b3913eacf..03aa19197c 100644 --- a/libs/cart/driver/src/interfaces/cart-address-service.interface.ts +++ b/libs/cart/driver/src/interfaces/cart-address-service.interface.ts @@ -1,22 +1,16 @@ import { InjectionToken } from '@angular/core'; import { Observable } from 'rxjs'; -import { - DaffCartAddress, - DaffCart, -} from '@daffodil/cart'; +import { DaffCart } from '@daffodil/cart'; /** * The interface responsible for managing the address of a cart. */ -export interface DaffCartAddressServiceInterface< - T extends DaffCartAddress = DaffCartAddress, - V extends DaffCart = DaffCart -> { +export interface DaffCartAddressServiceInterface { /** * Update the billing and shipping address of a cart */ - update(cartId: V['id'], address: Partial): Observable>; + update(cartId: T['id'], address: Partial): Observable>; } export const DaffCartAddressDriver = new InjectionToken< diff --git a/libs/cart/driver/src/interfaces/cart-billing-address-service.interface.ts b/libs/cart/driver/src/interfaces/cart-billing-address-service.interface.ts index ee8877fc80..92bc19758c 100644 --- a/libs/cart/driver/src/interfaces/cart-billing-address-service.interface.ts +++ b/libs/cart/driver/src/interfaces/cart-billing-address-service.interface.ts @@ -1,27 +1,21 @@ import { InjectionToken } from '@angular/core'; import { Observable } from 'rxjs'; -import { - DaffCartAddress, - DaffCart, -} from '@daffodil/cart'; +import { DaffCart } from '@daffodil/cart'; /** * The interface responsible for managing the billing address of a cart. */ -export interface DaffCartBillingAddressServiceInterface< - T extends DaffCartAddress = DaffCartAddress, - V extends DaffCart = DaffCart -> { +export interface DaffCartBillingAddressServiceInterface { /** * Retrieve the billing address of a cart */ - get(cartId: V['id']): Observable; + get(cartId: T['id']): Observable; /** * Update the billing address of a cart */ - update(cartId: V['id'], address: Partial): Observable>; + update(cartId: T['id'], address: Partial): Observable>; } export const DaffCartBillingAddressDriver = new InjectionToken< diff --git a/libs/cart/driver/src/interfaces/cart-coupon-service.interface.ts b/libs/cart/driver/src/interfaces/cart-coupon-service.interface.ts index a00572b14c..ca72f22223 100644 --- a/libs/cart/driver/src/interfaces/cart-coupon-service.interface.ts +++ b/libs/cart/driver/src/interfaces/cart-coupon-service.interface.ts @@ -1,29 +1,26 @@ import { InjectionToken } from '@angular/core'; import { Observable } from 'rxjs'; -import { - DaffCart, - DaffCartCoupon, -} from '@daffodil/cart'; +import { DaffCart } from '@daffodil/cart'; /** * The interface responsible for applying a coupon to a cart. */ -export interface DaffCartCouponServiceInterface { +export interface DaffCartCouponServiceInterface { /** * Apply a coupon to the cart and return a partial of the cart. */ - apply(cartId: T['id'], coupon: V): Observable>; + apply(cartId: T['id'], coupon: T['coupons'][number]): Observable>; /** * List coupon codes applied to a cart. */ - list(cartId: T['id']): Observable; + list(cartId: T['id']): Observable; /** * Remove a coupon from the cart and return a partial of the cart. */ - remove(cartId: T['id'], coupon: V): Observable>; + remove(cartId: T['id'], coupon: T['coupons'][number]): Observable>; /** * Remove all coupons from the cart and return a partial of the cart. diff --git a/libs/cart/driver/src/interfaces/cart-item-service.interface.ts b/libs/cart/driver/src/interfaces/cart-item-service.interface.ts index 98d5064c3a..f42f02f9fe 100644 --- a/libs/cart/driver/src/interfaces/cart-item-service.interface.ts +++ b/libs/cart/driver/src/interfaces/cart-item-service.interface.ts @@ -11,38 +11,37 @@ import { * The interface responsible for managing the items of a cart. */ export interface DaffCartItemServiceInterface< - T extends DaffCartItem = DaffCartItem, + T extends DaffCart = DaffCart, U extends DaffCartItemInput = DaffCartItemInput, - V extends DaffCart = DaffCart > { /** * List all of the available items of a cart */ - list(cartId: V['id']): Observable; + list(cartId: T['id']): Observable; /** * Get a specific cart item of a cart. */ - get(cartId: V['id'], item_id: DaffCartItem['id']): Observable; + get(cartId: T['id'], item_id: DaffCartItem['id']): Observable; /** * Add something to a cart. */ - add(id: V['id'], product: U): Observable>; + add(id: T['id'], product: U): Observable>; /** * Update an existing item in a cart */ update( - cartId: V['id'], - itemId: T['id'], - changes: Partial, - ): Observable>; + cartId: T['id'], + itemId: T['items'][number]['id'], + changes: Partial, + ): Observable>; /** * Remove an item from a cart. */ - delete(cartId: V['id'], itemId: T['id']): Observable>; + delete(cartId: T['id'], itemId: T['items'][number]['id']): Observable>; } export const DaffCartItemDriver = new InjectionToken< diff --git a/libs/cart/driver/src/interfaces/cart-order-service.interface.ts b/libs/cart/driver/src/interfaces/cart-order-service.interface.ts index f5986af8fb..592eefa7ed 100644 --- a/libs/cart/driver/src/interfaces/cart-order-service.interface.ts +++ b/libs/cart/driver/src/interfaces/cart-order-service.interface.ts @@ -3,7 +3,6 @@ import { Observable } from 'rxjs'; import { DaffCart, - DaffCartPaymentMethod, DaffCartOrderResult, } from '@daffodil/cart'; @@ -12,13 +11,12 @@ import { */ export interface DaffCartOrderServiceInterface< T extends DaffCart = DaffCart, - V extends DaffCartPaymentMethod = DaffCartPaymentMethod, R extends DaffCartOrderResult = DaffCartOrderResult > { /** * Place an order and return the order ID. */ - placeOrder(id: T['id'], payment?: V): Observable; + placeOrder(id: T['id'], payment?: T['payment']): Observable; } export const DaffCartOrderDriver = new InjectionToken( diff --git a/libs/cart/driver/src/interfaces/cart-payment-service.interface.ts b/libs/cart/driver/src/interfaces/cart-payment-service.interface.ts index 58b0aacbba..2d80259fc6 100644 --- a/libs/cart/driver/src/interfaces/cart-payment-service.interface.ts +++ b/libs/cart/driver/src/interfaces/cart-payment-service.interface.ts @@ -10,34 +10,30 @@ import { /** * The interface responsible for managing the selected payment method of a cart. */ -export interface DaffCartPaymentServiceInterface< - T extends DaffCartPaymentMethod = DaffCartPaymentMethod, - V extends DaffCart = DaffCart, - R extends DaffCartAddress = DaffCartAddress -> { +export interface DaffCartPaymentServiceInterface { /** * Get the currently applied payment method of a cart. */ - get(cartId: V['id']): Observable; + get(cartId: T['id']): Observable; /** * Update the payment method applied to a cart. * * If a billing address is provided, the driver will update that simultaneously. */ - update(cartId: V['id'], payment: Partial, billingAddress?: Partial): Observable>; + update(cartId: T['id'], payment: Partial, billingAddress?: Partial): Observable>; /** * Update the billing address and payment method applied to a cart. * * @deprecated use `update` with the `billingAddress` parameter instead. */ - updateWithBilling(cartId: V['id'], payment: Partial, address: Partial): Observable>; + updateWithBilling(cartId: T['id'], payment: Partial, address: Partial): Observable>; /** * Remove the payment method applied to a cart. */ - remove(cartId: V['id']): Observable; + remove(cartId: T['id']): Observable; } export const DaffCartPaymentDriver = new InjectionToken< diff --git a/libs/cart/driver/src/interfaces/cart-shipping-address-service.interface.ts b/libs/cart/driver/src/interfaces/cart-shipping-address-service.interface.ts index 165e1cb363..563f22d334 100644 --- a/libs/cart/driver/src/interfaces/cart-shipping-address-service.interface.ts +++ b/libs/cart/driver/src/interfaces/cart-shipping-address-service.interface.ts @@ -1,27 +1,21 @@ import { InjectionToken } from '@angular/core'; import { Observable } from 'rxjs'; -import { - DaffCartAddress, - DaffCart, -} from '@daffodil/cart'; +import { DaffCart } from '@daffodil/cart'; /** * The interface responsible for managing the shipping address of a cart. */ -export interface DaffCartShippingAddressServiceInterface< - T extends DaffCartAddress = DaffCartAddress, - V extends DaffCart = DaffCart -> { +export interface DaffCartShippingAddressServiceInterface { /** * Retrieve the shipping address of a cart. */ - get(cartId: V['id']): Observable; + get(cartId: T['id']): Observable; /** * Update the shipping address of a cart. */ - update(cartId: V['id'], address: Partial): Observable>; + update(cartId: T['id'], address: Partial): Observable>; } export const DaffCartShippingAddressDriver = new InjectionToken< diff --git a/libs/cart/driver/src/interfaces/cart-shipping-information-service.interface.ts b/libs/cart/driver/src/interfaces/cart-shipping-information-service.interface.ts index 7b1a955a46..4ef4125dfe 100644 --- a/libs/cart/driver/src/interfaces/cart-shipping-information-service.interface.ts +++ b/libs/cart/driver/src/interfaces/cart-shipping-information-service.interface.ts @@ -1,33 +1,27 @@ import { InjectionToken } from '@angular/core'; import { Observable } from 'rxjs'; -import { - DaffCartShippingRate, - DaffCart, -} from '@daffodil/cart'; +import { DaffCart } from '@daffodil/cart'; /** * The interface responsible for mediating the interaction of the shipping * information of a cart with a given platform. */ -export interface DaffCartShippingInformationServiceInterface< - T extends DaffCartShippingRate = DaffCartShippingRate, - V extends DaffCart = DaffCart ->{ +export interface DaffCartShippingInformationServiceInterface{ /** * Get the currently selected shipping method of a cart. */ - get(cartId: V['id']): Observable; + get(cartId: T['id']): Observable; /** * Update the currently selected shipping method of a cart. */ - update(cartId: V['id'], shippingInfo: Partial): Observable>; + update(cartId: T['id'], shippingInfo: Partial): Observable>; /** * Remove the currently selected shipping method from a cart. */ - delete(cartId: V['id'], id?: T['id']): Observable>; + delete(cartId: T['id'], id?: T['shipping_information']['id']): Observable>; } export const DaffCartShippingInformationDriver = new InjectionToken< diff --git a/libs/cart/src/helpers/get-affected-item.ts b/libs/cart/src/helpers/get-affected-item.ts new file mode 100644 index 0000000000..25ffd75753 --- /dev/null +++ b/libs/cart/src/helpers/get-affected-item.ts @@ -0,0 +1,14 @@ +import { DaffCartItem } from '../models/public_api'; + +/** + * Finds the IDs of any affected cart items between two carts. + */ +export function daffCartGetAffectedItems(oldItems: Array, newItems: Array): Array { + return newItems.reduce((acc, newItem) => { + const oldItem = oldItems.find(({ id }) => id === newItem.id); + if (!oldItem || oldItem.qty !== newItem.qty) { + acc.push(newItem.id); + } + return acc; + }, >[]); +} diff --git a/libs/cart/src/helpers/input-to-item-transform.ts b/libs/cart/src/helpers/input-to-item-transform.ts new file mode 100644 index 0000000000..4bf898b54a --- /dev/null +++ b/libs/cart/src/helpers/input-to-item-transform.ts @@ -0,0 +1,21 @@ +import { + DaffCartItem, + DaffCartItemInput, +} from '../models/public_api'; + +export const daffCartItemInputToItemTransform = (input: DaffCartItemInput): DaffCartItem => ({ + type: input.type, + product_id: input.productId, + qty: input.qty, + + item_id: null, + id: null, + parent_item_id: null, + sku: null, + name: null, + price: null, + row_total: null, + in_stock: false, + discounts: [], + url: null, +}); diff --git a/libs/cart/src/helpers/public_api.ts b/libs/cart/src/helpers/public_api.ts new file mode 100644 index 0000000000..57e8d6e682 --- /dev/null +++ b/libs/cart/src/helpers/public_api.ts @@ -0,0 +1,2 @@ +export * from './get-affected-item'; +export * from './input-to-item-transform'; diff --git a/libs/cart/src/public_api.ts b/libs/cart/src/public_api.ts index f8056edb80..ce13fc34ab 100644 --- a/libs/cart/src/public_api.ts +++ b/libs/cart/src/public_api.ts @@ -1,5 +1,6 @@ export * from './errors/public_api'; export * from './models/public_api'; export * from './injection-tokens/public_api'; +export * from './helpers/public_api'; export { DaffCartStorageService } from './storage/cart-storage.service'; diff --git a/libs/cart/state/src/actions/cart-address.actions.ts b/libs/cart/state/src/actions/cart-address.actions.ts index ded6d411bf..93f385fcf1 100644 --- a/libs/cart/state/src/actions/cart-address.actions.ts +++ b/libs/cart/state/src/actions/cart-address.actions.ts @@ -50,10 +50,7 @@ export class DaffCartAddressUpdateFailure implements DaffFailureAction { /** * A union of all the cart address action classes. */ -export type DaffCartAddressActions< - T extends DaffCartAddress = DaffCartAddress, - V extends DaffCart = DaffCart -> = - | DaffCartAddressUpdate - | DaffCartAddressUpdateSuccess +export type DaffCartAddressActions = + | DaffCartAddressUpdate + | DaffCartAddressUpdateSuccess | DaffCartAddressUpdateFailure; diff --git a/libs/cart/state/src/actions/cart-billing-address.actions.ts b/libs/cart/state/src/actions/cart-billing-address.actions.ts index c7ee91b5c5..a4d0e1da57 100644 --- a/libs/cart/state/src/actions/cart-billing-address.actions.ts +++ b/libs/cart/state/src/actions/cart-billing-address.actions.ts @@ -1,9 +1,6 @@ import { Action } from '@ngrx/store'; -import { - DaffCartAddress, - DaffCart, -} from '@daffodil/cart'; +import { DaffCart } from '@daffodil/cart'; import { DaffFailureAction, DaffStateError, @@ -33,10 +30,10 @@ export class DaffCartBillingAddressLoad implements Action { /** * Indicates the successful load of the cart's billing address. */ -export class DaffCartBillingAddressLoadSuccess implements Action { +export class DaffCartBillingAddressLoadSuccess implements Action { readonly type = DaffCartBillingAddressActionTypes.CartBillingAddressLoadSuccessAction; - constructor(public payload: T) {} + constructor(public payload: T['billing_address']) {} } /** @@ -51,10 +48,10 @@ export class DaffCartBillingAddressLoadFailure implements DaffFailureAction { /** * Triggers the update of the cart's billing address. */ -export class DaffCartBillingAddressUpdate implements Action { +export class DaffCartBillingAddressUpdate implements Action { readonly type = DaffCartBillingAddressActionTypes.CartBillingAddressUpdateAction; - constructor(public payload: Partial) {} + constructor(public payload: Partial) {} } /** @@ -79,12 +76,11 @@ export class DaffCartBillingAddressUpdateFailure implements DaffFailureAction { * A union of all the cart billing address action classes. */ export type DaffCartBillingAddressActions< - T extends DaffCartAddress = DaffCartAddress, - V extends DaffCart = DaffCart + T extends DaffCart = DaffCart, > = | DaffCartBillingAddressLoad | DaffCartBillingAddressLoadSuccess | DaffCartBillingAddressLoadFailure | DaffCartBillingAddressUpdate - | DaffCartBillingAddressUpdateSuccess + | DaffCartBillingAddressUpdateSuccess | DaffCartBillingAddressUpdateFailure; diff --git a/libs/cart/state/src/actions/cart-coupon.actions.ts b/libs/cart/state/src/actions/cart-coupon.actions.ts index d38530a9ab..e316637303 100644 --- a/libs/cart/state/src/actions/cart-coupon.actions.ts +++ b/libs/cart/state/src/actions/cart-coupon.actions.ts @@ -144,17 +144,14 @@ export class DaffCartCouponClearErrors implements Action { /** * A union of all the cart coupon action classes. */ -export type DaffCartCouponActions< - T extends DaffCart = DaffCart, - V extends DaffCartCoupon = DaffCartCoupon -> = - | DaffCartCouponApply +export type DaffCartCouponActions = + | DaffCartCouponApply | DaffCartCouponApplySuccess | DaffCartCouponApplyFailure | DaffCartCouponList - | DaffCartCouponListSuccess + | DaffCartCouponListSuccess | DaffCartCouponListFailure - | DaffCartCouponRemove + | DaffCartCouponRemove | DaffCartCouponRemoveSuccess | DaffCartCouponRemoveFailure | DaffCartCouponRemoveAll diff --git a/libs/cart/state/src/actions/cart-item.actions.ts b/libs/cart/state/src/actions/cart-item.actions.ts index eb212e204a..a8d3c85e85 100644 --- a/libs/cart/state/src/actions/cart-item.actions.ts +++ b/libs/cart/state/src/actions/cart-item.actions.ts @@ -2,6 +2,7 @@ import { Action } from '@ngrx/store'; import { DaffCart, + DaffCartItem, DaffCartItemInput, } from '@daffodil/cart'; import { @@ -10,7 +11,6 @@ import { } from '@daffodil/core/state'; import { DaffCartRetrievalAction } from '../cart-retrieval/public_api'; -import { DaffStatefulCartItem } from '../models/public_api'; /** * An enum for the cart item action types. @@ -47,7 +47,7 @@ export class DaffCartItemList implements Action { /** * Indicates the successful load of the cart's items. */ -export class DaffCartItemListSuccess implements Action { +export class DaffCartItemListSuccess implements Action { readonly type = DaffCartItemActionTypes.CartItemListSuccessAction; constructor(public payload: T[]) {} @@ -65,7 +65,7 @@ export class DaffCartItemListFailure implements DaffFailureAction { /** * Triggers the load of a specific cart item. */ -export class DaffCartItemLoad implements Action { +export class DaffCartItemLoad implements Action { readonly type = DaffCartItemActionTypes.CartItemLoadAction; constructor(public itemId: T['id']) {} @@ -74,7 +74,7 @@ export class DaffCartItemLoad implements Action { +export class DaffCartItemLoadSuccess implements Action { readonly type = DaffCartItemActionTypes.CartItemLoadSuccessAction; constructor(public payload: T) {} @@ -83,16 +83,16 @@ export class DaffCartItemLoadSuccess implements Action { +export class DaffCartItemLoadFailure implements Action { readonly type = DaffCartItemActionTypes.CartItemLoadFailureAction; - constructor(public payload: DaffStateError[], public itemId: T['id']) {} + constructor(public payload: DaffStateError[], public itemId: DaffCartItem['id']) {} } /** * Triggers the update of a specific cart item. */ -export class DaffCartItemUpdate implements Action { +export class DaffCartItemUpdate implements Action { readonly type = DaffCartItemActionTypes.CartItemUpdateAction; constructor(public itemId: T['id'], public changes: Partial) {} @@ -101,22 +101,19 @@ export class DaffCartItemUpdate implements Action { +export class DaffCartItemUpdateSuccess implements Action { readonly type = DaffCartItemActionTypes.CartItemUpdateSuccessAction; - constructor(public payload: Partial, public itemId: V['id']) {} + constructor(public payload: Partial, public itemId: T['items'][number]['id']) {} } /** * Indicates the failed update of a specific cart item. */ -export class DaffCartItemUpdateFailure implements Action { +export class DaffCartItemUpdateFailure implements Action { readonly type = DaffCartItemActionTypes.CartItemUpdateFailureAction; - constructor(public payload: DaffStateError[], public itemId: T['id']) {} + constructor(public payload: DaffStateError[], public itemId: DaffCartItem['id']) {} } /** @@ -125,7 +122,7 @@ export class DaffCartItemUpdateFailure implements Action { readonly type = DaffCartItemActionTypes.CartItemAddAction; - constructor(public input: T) {} + constructor(public input: T, public placeholderId?: string) {} } /** @@ -134,7 +131,7 @@ export class DaffCartItemAdd im export class DaffCartItemAddSuccess implements DaffCartRetrievalAction { readonly type = DaffCartItemActionTypes.CartItemAddSuccessAction; - constructor(public payload: Partial) {} + constructor(public payload: Partial, public itemId: DaffCartItem['id']) {} } /** @@ -143,13 +140,13 @@ export class DaffCartItemAddSuccess implements Da export class DaffCartItemAddFailure implements DaffFailureAction { readonly type = DaffCartItemActionTypes.CartItemAddFailureAction; - constructor(public payload: DaffStateError[]) {} + constructor(public payload: DaffStateError[], public placeholderId?: string) {} } /** * Triggers the deletion of a specific cart item. */ -export class DaffCartItemDelete implements Action { +export class DaffCartItemDelete implements Action { readonly type = DaffCartItemActionTypes.CartItemDeleteAction; constructor(public itemId: T['id']) {} @@ -167,10 +164,10 @@ export class DaffCartItemDeleteSuccess implements /** * Indicates the failed deletion of a specific cart item. */ -export class DaffCartItemDeleteFailure implements Action { +export class DaffCartItemDeleteFailure implements Action { readonly type = DaffCartItemActionTypes.CartItemDeleteFailureAction; - constructor(public payload: DaffStateError[], public itemId: T['id']) {} + constructor(public payload: DaffStateError[], public itemId: DaffCartItem['id']) {} } /** @@ -203,32 +200,33 @@ export class DaffCartItemDeleteOutOfStockFailure implements DaffFailureAction { */ export class DaffCartItemStateReset implements Action { readonly type = DaffCartItemActionTypes.CartItemStateResetAction; + + constructor(public itemId: DaffCartItem['id']) {} } /** * A union of all the cart item action classes. */ export type DaffCartItemActions< - T extends DaffStatefulCartItem = DaffStatefulCartItem, + T extends DaffCart = DaffCart, U extends DaffCartItemInput = DaffCartItemInput, - V extends DaffCart = DaffCart > = | DaffCartItemList - | DaffCartItemListSuccess + | DaffCartItemListSuccess | DaffCartItemListFailure - | DaffCartItemLoad - | DaffCartItemLoadSuccess - | DaffCartItemLoadFailure - | DaffCartItemUpdate - | DaffCartItemUpdateSuccess - | DaffCartItemUpdateFailure + | DaffCartItemLoad + | DaffCartItemLoadSuccess + | DaffCartItemLoadFailure + | DaffCartItemUpdate + | DaffCartItemUpdateSuccess + | DaffCartItemUpdateFailure | DaffCartItemAdd - | DaffCartItemAddSuccess + | DaffCartItemAddSuccess | DaffCartItemAddFailure - | DaffCartItemDelete - | DaffCartItemDeleteSuccess - | DaffCartItemDeleteFailure + | DaffCartItemDelete + | DaffCartItemDeleteSuccess + | DaffCartItemDeleteFailure | DaffCartItemDeleteOutOfStock - | DaffCartItemDeleteOutOfStockSuccess + | DaffCartItemDeleteOutOfStockSuccess | DaffCartItemDeleteOutOfStockFailure | DaffCartItemStateReset; diff --git a/libs/cart/state/src/actions/cart-order.actions.ts b/libs/cart/state/src/actions/cart-order.actions.ts index eee80e04a3..b751073eb1 100644 --- a/libs/cart/state/src/actions/cart-order.actions.ts +++ b/libs/cart/state/src/actions/cart-order.actions.ts @@ -3,6 +3,7 @@ import { Action } from '@ngrx/store'; import { DaffCartPaymentMethod, DaffCartOrderResult, + DaffCart, } from '@daffodil/cart'; import { DaffFailureAction, @@ -50,8 +51,8 @@ export class DaffCartPlaceOrderFailure implements DaffFailureAction { */ export type DaffCartOrderActions< T extends DaffCartOrderResult = DaffCartOrderResult, - V extends DaffCartPaymentMethod = DaffCartPaymentMethod + V extends DaffCart = DaffCart > = - | DaffCartPlaceOrder + | DaffCartPlaceOrder | DaffCartPlaceOrderSuccess | DaffCartPlaceOrderFailure; diff --git a/libs/cart/state/src/actions/cart-payment.actions.ts b/libs/cart/state/src/actions/cart-payment.actions.ts index 0aaea3a794..3a46d85e66 100644 --- a/libs/cart/state/src/actions/cart-payment.actions.ts +++ b/libs/cart/state/src/actions/cart-payment.actions.ts @@ -41,10 +41,10 @@ export class DaffCartPaymentLoad implements Action { /** * Indicates the successful load of the cart's selected payment method. */ -export class DaffCartPaymentLoadSuccess implements Action { +export class DaffCartPaymentLoadSuccess implements Action { readonly type = DaffCartPaymentActionTypes.CartPaymentLoadSuccessAction; - constructor(public payload: T) {} + constructor(public payload: T['payment']) {} } /** @@ -59,10 +59,10 @@ export class DaffCartPaymentLoadFailure implements DaffFailureAction { /** * Triggers the update of the cart's selected payment method. */ -export class DaffCartPaymentUpdate implements Action { +export class DaffCartPaymentUpdate implements Action { readonly type = DaffCartPaymentActionTypes.CartPaymentUpdateAction; - constructor(public payload: Partial) {} + constructor(public payload: Partial) {} } /** @@ -89,13 +89,10 @@ export class DaffCartPaymentUpdateFailure implements DaffFailureAction { * @param payment The payment method. * @param address The billing address. */ -export class DaffCartPaymentUpdateWithBilling< - T extends DaffCartPaymentMethod = DaffCartPaymentMethod, - R extends DaffCartAddress = DaffCartAddress -> implements Action { +export class DaffCartPaymentUpdateWithBilling implements Action { readonly type = DaffCartPaymentActionTypes.CartPaymentUpdateWithBillingAction; - constructor(public payment: Partial, public address: Partial) {} + constructor(public payment: Partial, public address: Partial) {} } /** @@ -149,28 +146,24 @@ export class DaffCartPaymentRemoveFailure implements DaffFailureAction { * * todo: remove when possible. */ -export class DaffCartPaymentMethodAdd implements Action { +export class DaffCartPaymentMethodAdd implements Action { readonly type = DaffCartPaymentActionTypes.CartPaymentMethodAddAction; - constructor(public payload: T) {} + constructor(public payload: T['payment']) {} } /** * A union of all the cart payment action classes. */ -export type DaffCartPaymentActions< - T extends DaffCartPaymentMethod = DaffCartPaymentMethod, - V extends DaffCart = DaffCart, - R extends DaffCartAddress = DaffCartAddress, -> = +export type DaffCartPaymentActions = | DaffCartPaymentLoad | DaffCartPaymentLoadSuccess | DaffCartPaymentLoadFailure | DaffCartPaymentUpdate - | DaffCartPaymentUpdateSuccess + | DaffCartPaymentUpdateSuccess | DaffCartPaymentUpdateFailure - | DaffCartPaymentUpdateWithBilling - | DaffCartPaymentUpdateWithBillingSuccess + | DaffCartPaymentUpdateWithBilling + | DaffCartPaymentUpdateWithBillingSuccess | DaffCartPaymentUpdateWithBillingFailure | DaffCartPaymentRemove | DaffCartPaymentRemoveSuccess diff --git a/libs/cart/state/src/actions/cart-shipping-address.actions.ts b/libs/cart/state/src/actions/cart-shipping-address.actions.ts index 9ad42f1442..e3a58f2e03 100644 --- a/libs/cart/state/src/actions/cart-shipping-address.actions.ts +++ b/libs/cart/state/src/actions/cart-shipping-address.actions.ts @@ -1,9 +1,6 @@ import { Action } from '@ngrx/store'; -import { - DaffCartAddress, - DaffCart, -} from '@daffodil/cart'; +import { DaffCart } from '@daffodil/cart'; import { DaffFailureAction, DaffStateError, @@ -33,10 +30,10 @@ export class DaffCartShippingAddressLoad implements Action { /** * Indicates the successful load of the cart's shipping address. */ -export class DaffCartShippingAddressLoadSuccess implements Action { +export class DaffCartShippingAddressLoadSuccess implements Action { readonly type = DaffCartShippingAddressActionTypes.CartShippingAddressLoadSuccessAction; - constructor(public payload: T) {} + constructor(public payload: T['shipping_address']) {} } /** @@ -51,10 +48,10 @@ export class DaffCartShippingAddressLoadFailure implements DaffFailureAction { /** * Triggers the update of the cart's shipping address. */ -export class DaffCartShippingAddressUpdate implements Action { +export class DaffCartShippingAddressUpdate implements Action { readonly type = DaffCartShippingAddressActionTypes.CartShippingAddressUpdateAction; - constructor(public payload: Partial) {} + constructor(public payload: Partial) {} } /** @@ -78,13 +75,10 @@ export class DaffCartShippingAddressUpdateFailure implements DaffFailureAction { /** * A union of all the cart shipping address action classes. */ -export type DaffCartShippingAddressActions< - T extends DaffCartAddress = DaffCartAddress, - V extends DaffCart = DaffCart -> = +export type DaffCartShippingAddressActions = | DaffCartShippingAddressLoad | DaffCartShippingAddressLoadSuccess | DaffCartShippingAddressLoadFailure | DaffCartShippingAddressUpdate - | DaffCartShippingAddressUpdateSuccess + | DaffCartShippingAddressUpdateSuccess | DaffCartShippingAddressUpdateFailure; diff --git a/libs/cart/state/src/actions/cart-shipping-information.actions.ts b/libs/cart/state/src/actions/cart-shipping-information.actions.ts index ff13c013fc..f84cd986b9 100644 --- a/libs/cart/state/src/actions/cart-shipping-information.actions.ts +++ b/libs/cart/state/src/actions/cart-shipping-information.actions.ts @@ -81,10 +81,10 @@ export class DaffCartShippingInformationUpdateFailure implements DaffFailureActi /** * Triggers the deletion of the cart's shipping information. */ -export class DaffCartShippingInformationDelete implements Action { +export class DaffCartShippingInformationDelete implements Action { readonly type = DaffCartShippingInformationActionTypes.CartShippingInformationDeleteAction; - constructor(public id?: T['id']) {} + constructor(public id?: DaffCartShippingRate['id']) {} } export class DaffCartShippingInformationDeleteSuccess implements DaffCartRetrievalAction { @@ -102,16 +102,13 @@ export class DaffCartShippingInformationDeleteFailure implements DaffFailureActi /** * A union of all the cart shipping information action classes. */ -export type DaffCartShippingInformationActions< - T extends DaffCartShippingRate = DaffCartShippingRate, - V extends DaffCart = DaffCart -> = +export type DaffCartShippingInformationActions = | DaffCartShippingInformationLoad - | DaffCartShippingInformationLoadSuccess + | DaffCartShippingInformationLoadSuccess | DaffCartShippingInformationLoadFailure - | DaffCartShippingInformationUpdate - | DaffCartShippingInformationUpdateSuccess + | DaffCartShippingInformationUpdate + | DaffCartShippingInformationUpdateSuccess | DaffCartShippingInformationUpdateFailure - | DaffCartShippingInformationDelete - | DaffCartShippingInformationDeleteSuccess + | DaffCartShippingInformationDelete + | DaffCartShippingInformationDeleteSuccess | DaffCartShippingInformationDeleteFailure; diff --git a/libs/cart/state/src/effects/cart-address.effects.spec.ts b/libs/cart/state/src/effects/cart-address.effects.spec.ts index 2d8310e173..369a4a961c 100644 --- a/libs/cart/state/src/effects/cart-address.effects.spec.ts +++ b/libs/cart/state/src/effects/cart-address.effects.spec.ts @@ -39,7 +39,7 @@ import { DaffCartAddressEffects } from './cart-address.effects'; describe('@daffodil/cart/state | DaffCartAddressEffects', () => { let actions$: Observable; - let effects: DaffCartAddressEffects; + let effects: DaffCartAddressEffects; let mockCart: DaffCart; let mockCartAddress: DaffCartAddress; @@ -69,13 +69,13 @@ describe('@daffodil/cart/state | DaffCartAddressEffects', () => { ], }); - effects = TestBed.inject>(DaffCartAddressEffects); + effects = TestBed.inject(DaffCartAddressEffects); daffAddressDriver = TestBed.inject(DaffCartAddressDriver); daffCartStorageService = TestBed.inject(DaffCartStorageService); - cartFactory = TestBed.inject(DaffCartFactory); - cartAddressFactory = TestBed.inject(DaffCartAddressFactory); + cartFactory = TestBed.inject(DaffCartFactory); + cartAddressFactory = TestBed.inject(DaffCartAddressFactory); mockCart = cartFactory.create(); mockCartAddress = cartAddressFactory.create(); diff --git a/libs/cart/state/src/effects/cart-address.effects.ts b/libs/cart/state/src/effects/cart-address.effects.ts index f4cd98869f..cb6e24d99b 100644 --- a/libs/cart/state/src/effects/cart-address.effects.ts +++ b/libs/cart/state/src/effects/cart-address.effects.ts @@ -14,11 +14,9 @@ import { import { switchMap, map, - catchError, } from 'rxjs/operators'; import { - DaffCartAddress, DaffCart, DaffCartStorageService, } from '@daffodil/cart'; @@ -28,33 +26,32 @@ import { } from '@daffodil/cart/driver'; import { DAFF_STORAGE_SERVICE_ERROR_CODE, - DaffStorageServiceError, catchAndArrayifyErrors, } from '@daffodil/core'; import { ErrorTransformer } from '@daffodil/core/state'; import { DaffCartAddressActionTypes, - DaffCartAddressUpdate, DaffCartAddressUpdateSuccess, DaffCartAddressUpdateFailure, DaffCartStorageFailure, + DaffCartAddressActions, } from '../actions/public_api'; import { DAFF_CART_ERROR_MATCHER } from '../injection-tokens/public_api'; @Injectable() -export class DaffCartAddressEffects { +export class DaffCartAddressEffects { constructor( - private actions$: Actions, + private actions$: Actions, @Inject(DAFF_CART_ERROR_MATCHER) private errorMatcher: ErrorTransformer, - @Inject(DaffCartAddressDriver) private driver: DaffCartAddressServiceInterface, + @Inject(DaffCartAddressDriver) private driver: DaffCartAddressServiceInterface, private storage: DaffCartStorageService, ) {} update$ = createEffect(() => this.actions$.pipe( ofType(DaffCartAddressActionTypes.CartAddressUpdateAction), - switchMap((action: DaffCartAddressUpdate) => defer(() => of(this.storage.getCartId())).pipe( + switchMap((action) => defer(() => of(this.storage.getCartId())).pipe( switchMap(cartId => this.driver.update(cartId, action.payload)), map((resp) => new DaffCartAddressUpdateSuccess(resp)), catchAndArrayifyErrors(error => of(error.find((err) => err.code === DAFF_STORAGE_SERVICE_ERROR_CODE) diff --git a/libs/cart/state/src/effects/cart-billing-address.effects.spec.ts b/libs/cart/state/src/effects/cart-billing-address.effects.spec.ts index 4a4ac20e73..29b617eff4 100644 --- a/libs/cart/state/src/effects/cart-billing-address.effects.spec.ts +++ b/libs/cart/state/src/effects/cart-billing-address.effects.spec.ts @@ -37,7 +37,7 @@ import { DaffCartBillingAddressEffects } from './cart-billing-address.effects'; describe('@daffodil/cart/state | DaffCartBillingAddressEffects', () => { let actions$: Observable; - let effects: DaffCartBillingAddressEffects; + let effects: DaffCartBillingAddressEffects; let mockCart: DaffCart; let mockCartBillingAddress: DaffCartAddress; @@ -63,13 +63,13 @@ describe('@daffodil/cart/state | DaffCartBillingAddressEffects', () => { ], }); - effects = TestBed.inject>(DaffCartBillingAddressEffects); + effects = TestBed.inject(DaffCartBillingAddressEffects); daffBillingAddressDriver = TestBed.inject(DaffCartBillingAddressDriver); daffCartStorageService = TestBed.inject(DaffCartStorageService); - cartFactory = TestBed.inject(DaffCartFactory); - cartAddressFactory = TestBed.inject(DaffCartAddressFactory); + cartFactory = TestBed.inject(DaffCartFactory); + cartAddressFactory = TestBed.inject(DaffCartAddressFactory); mockCart = cartFactory.create(); mockCartBillingAddress = cartAddressFactory.create(); diff --git a/libs/cart/state/src/effects/cart-billing-address.effects.ts b/libs/cart/state/src/effects/cart-billing-address.effects.ts index 69931a567b..efc923fe0c 100644 --- a/libs/cart/state/src/effects/cart-billing-address.effects.ts +++ b/libs/cart/state/src/effects/cart-billing-address.effects.ts @@ -11,11 +11,9 @@ import { of } from 'rxjs'; import { switchMap, map, - catchError, } from 'rxjs/operators'; import { - DaffCartAddress, DaffCart, DaffCartStorageService, } from '@daffodil/cart'; @@ -28,30 +26,29 @@ import { ErrorTransformer } from '@daffodil/core/state'; import { DaffCartBillingAddressActionTypes, - DaffCartBillingAddressLoad, DaffCartBillingAddressLoadSuccess, DaffCartBillingAddressLoadFailure, - DaffCartBillingAddressUpdate, DaffCartBillingAddressUpdateSuccess, DaffCartBillingAddressUpdateFailure, + DaffCartBillingAddressActions, } from '../actions/public_api'; import { DAFF_CART_ERROR_MATCHER } from '../injection-tokens/public_api'; @Injectable() -export class DaffCartBillingAddressEffects { +export class DaffCartBillingAddressEffects { constructor( - private actions$: Actions, + private actions$: Actions>, @Inject(DAFF_CART_ERROR_MATCHER) private errorMatcher: ErrorTransformer, - @Inject(DaffCartBillingAddressDriver) private driver: DaffCartBillingAddressServiceInterface, + @Inject(DaffCartBillingAddressDriver) private driver: DaffCartBillingAddressServiceInterface, private storage: DaffCartStorageService, ) {} get$ = createEffect(() => this.actions$.pipe( ofType(DaffCartBillingAddressActionTypes.CartBillingAddressLoadAction), - switchMap((action: DaffCartBillingAddressLoad) => + switchMap((action) => this.driver.get(this.storage.getCartId()).pipe( - map((resp: T) => new DaffCartBillingAddressLoadSuccess(resp)), + map((resp) => new DaffCartBillingAddressLoadSuccess(resp)), catchAndArrayifyErrors(error => of(new DaffCartBillingAddressLoadFailure(error.map(this.errorMatcher)))), ), ), @@ -60,9 +57,9 @@ export class DaffCartBillingAddressEffects this.actions$.pipe( ofType(DaffCartBillingAddressActionTypes.CartBillingAddressUpdateAction), - switchMap((action: DaffCartBillingAddressUpdate) => + switchMap((action) => this.driver.update(this.storage.getCartId(), action.payload).pipe( - map((resp: V) => new DaffCartBillingAddressUpdateSuccess(resp)), + map((resp) => new DaffCartBillingAddressUpdateSuccess(resp)), catchAndArrayifyErrors(error => of(new DaffCartBillingAddressUpdateFailure(error.map(this.errorMatcher)))), ), ), diff --git a/libs/cart/state/src/effects/cart-coupon.effects.ts b/libs/cart/state/src/effects/cart-coupon.effects.ts index 4f39fba4c1..194868b54f 100644 --- a/libs/cart/state/src/effects/cart-coupon.effects.ts +++ b/libs/cart/state/src/effects/cart-coupon.effects.ts @@ -14,12 +14,10 @@ import { import { switchMap, map, - catchError, } from 'rxjs/operators'; import { DaffCart, - DaffCartCoupon, DaffCartStorageService, } from '@daffodil/cart'; import { @@ -28,45 +26,38 @@ import { } from '@daffodil/cart/driver'; import { DAFF_STORAGE_SERVICE_ERROR_CODE, - DaffStorageServiceError, catchAndArrayifyErrors, } from '@daffodil/core'; import { ErrorTransformer } from '@daffodil/core/state'; import { DaffCartCouponActionTypes, - DaffCartCouponList, DaffCartCouponListSuccess, DaffCartCouponListFailure, DaffCartCouponRemoveSuccess, DaffCartCouponRemoveFailure, - DaffCartCouponRemove, - DaffCartCouponRemoveAll, DaffCartCouponRemoveAllSuccess, DaffCartCouponRemoveAllFailure, - DaffCartCouponApply, DaffCartCouponApplySuccess, DaffCartCouponApplyFailure, DaffCartStorageFailure, + DaffCartCouponActions, } from '../actions/public_api'; import { DAFF_CART_ERROR_MATCHER } from '../injection-tokens/public_api'; @Injectable() -export class DaffCartCouponEffects< - T extends DaffCart = DaffCart, - V extends DaffCartCoupon = DaffCartCoupon -> { +export class DaffCartCouponEffects { constructor( - private actions$: Actions, + private actions$: Actions, @Inject(DAFF_CART_ERROR_MATCHER) private errorMatcher: ErrorTransformer, - @Inject(DaffCartCouponDriver) private driver: DaffCartCouponServiceInterface, + @Inject(DaffCartCouponDriver) private driver: DaffCartCouponServiceInterface, private storage: DaffCartStorageService, ) {} apply$ = createEffect(() => this.actions$.pipe( ofType(DaffCartCouponActionTypes.CartCouponApplyAction), - switchMap((action: DaffCartCouponApply) => defer(() => of(this.storage.getCartId())).pipe( + switchMap((action) => defer(() => of(this.storage.getCartId())).pipe( switchMap(cartId => this.driver.apply(cartId, action.payload)), map(resp => new DaffCartCouponApplySuccess(resp)), catchAndArrayifyErrors(error => of(error.find((err) => err.code === DAFF_STORAGE_SERVICE_ERROR_CODE) @@ -79,9 +70,9 @@ export class DaffCartCouponEffects< list$ = createEffect(() => this.actions$.pipe( ofType(DaffCartCouponActionTypes.CartCouponListAction), - switchMap((action: DaffCartCouponList) => defer(() => of(this.storage.getCartId())).pipe( + switchMap((action) => defer(() => of(this.storage.getCartId())).pipe( switchMap(cartId => this.driver.list(cartId)), - map(resp => new DaffCartCouponListSuccess(resp)), + map(resp => new DaffCartCouponListSuccess(resp)), catchAndArrayifyErrors(error => of(error.find((err) => err.code === DAFF_STORAGE_SERVICE_ERROR_CODE) ? new DaffCartStorageFailure(error.map(this.errorMatcher)) : new DaffCartCouponListFailure(error.map(this.errorMatcher)), @@ -92,7 +83,7 @@ export class DaffCartCouponEffects< remove$ = createEffect(() => this.actions$.pipe( ofType(DaffCartCouponActionTypes.CartCouponRemoveAction), - switchMap((action: DaffCartCouponRemove) => defer(() => of(this.storage.getCartId())).pipe( + switchMap((action) => defer(() => of(this.storage.getCartId())).pipe( switchMap(cartId => this.driver.remove(cartId, action.payload)), map(resp => new DaffCartCouponRemoveSuccess(resp)), catchAndArrayifyErrors(error => of(error.find((err) => err.code === DAFF_STORAGE_SERVICE_ERROR_CODE) @@ -105,7 +96,7 @@ export class DaffCartCouponEffects< removeAll$ = createEffect(() => this.actions$.pipe( ofType(DaffCartCouponActionTypes.CartCouponRemoveAllAction), - switchMap((action: DaffCartCouponRemoveAll) => defer(() => of(this.storage.getCartId())).pipe( + switchMap((action) => defer(() => of(this.storage.getCartId())).pipe( switchMap(cartId => this.driver.removeAll(cartId)), map(resp => new DaffCartCouponRemoveAllSuccess(resp)), catchAndArrayifyErrors(error => of(error.find((err) => err.code === DAFF_STORAGE_SERVICE_ERROR_CODE) diff --git a/libs/cart/state/src/effects/cart-item.effects.spec.ts b/libs/cart/state/src/effects/cart-item.effects.spec.ts index c539fe5d8f..a45a6727f8 100644 --- a/libs/cart/state/src/effects/cart-item.effects.spec.ts +++ b/libs/cart/state/src/effects/cart-item.effects.spec.ts @@ -21,6 +21,7 @@ import { DaffCart, DaffCartStorageService, DaffCartItemInputType, + DaffCartItem, } from '@daffodil/cart'; import { DaffCartItemServiceInterface, @@ -44,7 +45,6 @@ import { DaffCartItemDelete, DaffCartItemDeleteSuccess, DaffCartItemDeleteFailure, - DaffStatefulCartItem, DaffCartItemStateReset, DaffCartItemStateDebounceTime, DaffCartItemDeleteOutOfStock, @@ -66,6 +66,7 @@ import { import { daffComposeReducers, daffIdentityReducer, + DaffOperationEntity, DaffStateError, } from '@daffodil/core/state'; @@ -73,11 +74,11 @@ import { DaffCartItemEffects } from './cart-item.effects'; describe('@daffodil/cart/state | DaffCartItemEffects', () => { let actions$: Observable; - let effects: DaffCartItemEffects; + let effects: DaffCartItemEffects; let store: Store; let mockCart: DaffCart; - let mockCartItem: DaffStatefulCartItem; + let mockCartItem: DaffOperationEntity; let mockCartItemInput: DaffCartItemInput; let cartFactory: DaffCartFactory; @@ -125,11 +126,7 @@ describe('@daffodil/cart/state | DaffCartItemEffects', () => { ], }); - effects = TestBed.inject>(DaffCartItemEffects); + effects = TestBed.inject(DaffCartItemEffects); store = TestBed.inject(Store); daffCartItemDriver = TestBed.inject(DaffCartItemDriver); @@ -249,7 +246,7 @@ describe('@daffodil/cart/state | DaffCartItemEffects', () => { beforeEach(() => { mockCart.items.push(mockCartItem); driverAddSpy.and.returnValue(of(mockCart)); - const cartItemAddSuccessAction = new DaffCartItemAddSuccess(mockCart); + const cartItemAddSuccessAction = new DaffCartItemAddSuccess(mockCart, mockCartItem.id); actions$ = hot('--a', { a: cartItemAddAction }); expected = cold('--b', { b: cartItemAddSuccessAction }); }); @@ -360,8 +357,8 @@ describe('@daffodil/cart/state | DaffCartItemEffects', () => { }); testScheduler.run(helpers => { const expectedMarble = '4000ms a'; - const cartItemAddSuccess = new DaffCartItemAddSuccess(mockCart); - const shopCartItemReset = new DaffCartItemStateReset(); + const cartItemAddSuccess = new DaffCartItemAddSuccess(mockCart, mockCartItem.id); + const shopCartItemReset = new DaffCartItemStateReset(mockCartItem.id); actions$ = helpers.hot('a', { a: cartItemAddSuccess }); expectedObservable = { a: shopCartItemReset }; @@ -376,10 +373,10 @@ describe('@daffodil/cart/state | DaffCartItemEffects', () => { }); testScheduler.run(helpers => { const expectedMarble = '4000ms ----a'; - const cartItemAddSuccess = new DaffCartItemAddSuccess(mockCart); + const cartItemAddSuccess = new DaffCartItemAddSuccess(mockCart, mockCartItem.id); const cartItemUpdateAction = new DaffCartItemUpdate(mockCartItem.id, mockCartItem); const cartItemUpdateSuccessAction = new DaffCartItemUpdateSuccess(mockCart, mockCartItem.id); - const shopCartItemReset = new DaffCartItemStateReset(); + const shopCartItemReset = new DaffCartItemStateReset(mockCartItem.id); actions$ = helpers.hot('a-b-c', { a: cartItemAddSuccess, b: cartItemUpdateAction, c: cartItemUpdateSuccessAction }); expectedObservable = { a: shopCartItemReset }; @@ -398,7 +395,7 @@ describe('@daffodil/cart/state | DaffCartItemEffects', () => { testScheduler.run(helpers => { const expectedMarble = '4000ms a'; const cartItemUpdateSuccess = new DaffCartItemUpdateSuccess(mockCart, mockCartItem.id); - const shopCartItemReset = new DaffCartItemStateReset(); + const shopCartItemReset = new DaffCartItemStateReset(mockCartItem.id); actions$ = helpers.hot('a', { a: cartItemUpdateSuccess }); expectedObservable = { a: shopCartItemReset }; @@ -464,7 +461,7 @@ describe('@daffodil/cart/state | DaffCartItemEffects', () => { let expected; let cartItemDeleteOutOfStockAction: DaffCartItemDeleteOutOfStock; let cartItemDeleteOutOfStockSuccessAction: DaffCartItemDeleteOutOfStockSuccess; - let outOfStockCartItems: DaffStatefulCartItem[]; + let outOfStockCartItems: DaffOperationEntity[]; beforeEach(() => { cartItemDeleteOutOfStockAction = new DaffCartItemDeleteOutOfStock(); diff --git a/libs/cart/state/src/effects/cart-item.effects.ts b/libs/cart/state/src/effects/cart-item.effects.ts index d7796eb23e..cbf968d1c2 100644 --- a/libs/cart/state/src/effects/cart-item.effects.ts +++ b/libs/cart/state/src/effects/cart-item.effects.ts @@ -7,10 +7,6 @@ import { createEffect, ofType, } from '@ngrx/effects'; -import { - select, - Store, -} from '@ngrx/store'; import { combineLatest, of, @@ -18,7 +14,6 @@ import { import { switchMap, map, - catchError, debounceTime, mergeMap, take, @@ -29,6 +24,7 @@ import { DaffCartItemInput, DaffCart, DaffCartStorageService, + daffCartGetAffectedItems, } from '@daffodil/cart'; import { DaffCartDriverResolveService, @@ -41,82 +37,60 @@ import { ErrorTransformer } from '@daffodil/core/state'; import { DaffCartItemActionTypes, - DaffCartItemLoad, DaffCartItemLoadSuccess, DaffCartItemLoadFailure, - DaffCartItemDelete, DaffCartItemDeleteSuccess, DaffCartItemDeleteFailure, - DaffCartItemUpdate, DaffCartItemUpdateSuccess, DaffCartItemUpdateFailure, - DaffCartItemList, DaffCartItemListSuccess, DaffCartItemListFailure, DaffCartItemAddSuccess, DaffCartItemAddFailure, DaffCartItemStateReset, - DaffCartItemDeleteOutOfStock, DaffCartItemDeleteOutOfStockSuccess, DaffCartItemDeleteOutOfStockFailure, DaffCartItemActions, } from '../actions/public_api'; +import { DaffCartFacade } from '../facades/cart/cart.facade'; import { DaffCartItemStateDebounceTime } from '../injection-tokens/cart-item-state-debounce-time'; import { DAFF_CART_ERROR_MATCHER } from '../injection-tokens/public_api'; -import { DaffStatefulCartItem } from '../models/public_api'; -import { getDaffCartSelectors } from '../selectors/public_api'; @Injectable() export class DaffCartItemEffects< - T extends DaffStatefulCartItem, - U extends DaffCartItemInput, - V extends DaffCart, + T extends DaffCart = DaffCart, + U extends DaffCartItemInput = DaffCartItemInput, > { constructor( - private actions$: Actions>, + private actions$: Actions>, @Inject(DAFF_CART_ERROR_MATCHER) private errorMatcher: ErrorTransformer, - @Inject(DaffCartItemDriver) private driver: DaffCartItemServiceInterface, + @Inject(DaffCartItemDriver) private driver: DaffCartItemServiceInterface, private storage: DaffCartStorageService, @Inject(DaffCartItemStateDebounceTime) private cartItemStateDebounceTime: number, - private store: Store, - private cartResolver: DaffCartDriverResolveService, + private cartResolver: DaffCartDriverResolveService, + private cartFacade: DaffCartFacade, ) {} - list$ = createEffect(() => this.actions$.pipe( ofType(DaffCartItemActionTypes.CartItemListAction), - switchMap((action: DaffCartItemList) => + switchMap((action) => this.driver.list(this.storage.getCartId()).pipe( - map((resp: T[]) => new DaffCartItemListSuccess(resp)), + map((resp) => new DaffCartItemListSuccess(resp)), catchAndArrayifyErrors(error => of(new DaffCartItemListFailure(error.map(this.errorMatcher)))), ), ), )); - get$ = createEffect(() => this.actions$.pipe( ofType(DaffCartItemActionTypes.CartItemLoadAction), - switchMap((action: DaffCartItemLoad) => + switchMap((action) => this.driver.get(this.storage.getCartId(), action.itemId).pipe( - map((resp: T) => new DaffCartItemLoadSuccess(resp)), + map((resp) => new DaffCartItemLoadSuccess(resp)), catchAndArrayifyErrors(error => of(new DaffCartItemLoadFailure(error.map(this.errorMatcher), action.itemId))), ), ), )); - private addCartItem$( - cartId: DaffCart['id'], - input: U, - ) { - return this.driver.add( - cartId, - input, - ).pipe( - map((resp) => new DaffCartItemAddSuccess(resp)), - catchAndArrayifyErrors(error => of(new DaffCartItemAddFailure(error.map(this.errorMatcher)))), - ); - } - add$ = createEffect(() => this.actions$.pipe( ofType(DaffCartItemActionTypes.CartItemAddAction), concatMap((action) => combineLatest([ @@ -124,51 +98,54 @@ export class DaffCartItemEffects< this.cartResolver.getCartIdOrFail(), ])), mergeMap(([action, id]) => - this.addCartItem$( - id, - action.input, + combineLatest([ + this.driver.add( + id, + action.input, + ), + this.cartFacade.cart$.pipe( + take(1), + ), + ]).pipe( + map(([newCart, oldCart]) => new DaffCartItemAddSuccess( + newCart, + daffCartGetAffectedItems(oldCart.items, newCart.items)[0], + )), + catchAndArrayifyErrors(error => of(new DaffCartItemAddFailure(error.map(this.errorMatcher), action.placeholderId))), ), ), daffCartDriverHandleCartNotFound(this.storage), + // TODO: figure out how to get placeholder ID here catchAndArrayifyErrors(error => of(new DaffCartItemAddFailure(error.map(this.errorMatcher)))), )); - update$ = createEffect(() => this.actions$.pipe( ofType(DaffCartItemActionTypes.CartItemUpdateAction), - mergeMap((action: DaffCartItemUpdate) => + mergeMap((action) => this.driver.update( this.storage.getCartId(), action.itemId, action.changes, ).pipe( - map((resp: V) => new DaffCartItemUpdateSuccess(resp, action.itemId)), + map((resp) => new DaffCartItemUpdateSuccess(resp, action.itemId)), catchAndArrayifyErrors(error => of(new DaffCartItemUpdateFailure(error.map(this.errorMatcher), action.itemId))), ), ), )); - resetCartItemStateAfterChange$ = createEffect(() => this.actions$.pipe( - ofType( - // these actions will reset the debounce interval - DaffCartItemActionTypes.CartItemAddSuccessAction, - DaffCartItemActionTypes.CartItemUpdateSuccessAction, - DaffCartItemActionTypes.CartItemUpdateAction, - ), - debounceTime(this.cartItemStateDebounceTime), ofType( // these actions will cause the cart item state reset DaffCartItemActionTypes.CartItemAddSuccessAction, DaffCartItemActionTypes.CartItemUpdateSuccessAction, ), - map(() => new DaffCartItemStateReset()), + debounceTime(this.cartItemStateDebounceTime), + map((action) => new DaffCartItemStateReset(action.itemId)), )); - delete$ = createEffect(() => this.actions$.pipe( ofType(DaffCartItemActionTypes.CartItemDeleteAction), - mergeMap((action: DaffCartItemDelete) => + mergeMap((action) => this.driver.delete(this.storage.getCartId(), action.itemId).pipe( map((resp) => new DaffCartItemDeleteSuccess(resp)), catchAndArrayifyErrors(error => of(new DaffCartItemDeleteFailure(error.map(this.errorMatcher), action.itemId))), @@ -176,23 +153,20 @@ export class DaffCartItemEffects< ), )); - removeOutOfStock$ = createEffect(() => this.actions$.pipe( ofType(DaffCartItemActionTypes.CartItemDeleteOutOfStockAction), - switchMap((action: DaffCartItemDeleteOutOfStock) => this.store.pipe( - select(getDaffCartSelectors().selectOutOfStockCartItems), + switchMap((action) => this.cartFacade.outOfStockItems$.pipe( take(1), )), switchMap(items => items.length > 0 ? combineLatest(items.map(item => this.driver.delete(this.storage.getCartId(), item.id))).pipe( - map(partialCarts => Object.assign({}, ...partialCarts)), + map(partialCarts => Object.assign({}, ...partialCarts)), ) - : this.store.pipe( - select(getDaffCartSelectors().selectCartValue), + : this.cartFacade.cart$.pipe( take(1), ), ), - map(cart => new DaffCartItemDeleteOutOfStockSuccess(cart)), + map((cart) => new DaffCartItemDeleteOutOfStockSuccess(cart)), catchAndArrayifyErrors(error => of(new DaffCartItemDeleteOutOfStockFailure(error.map(this.errorMatcher)))), )); } diff --git a/libs/cart/state/src/effects/cart-order.effects.ts b/libs/cart/state/src/effects/cart-order.effects.ts index 62b2a81020..9c469d1778 100644 --- a/libs/cart/state/src/effects/cart-order.effects.ts +++ b/libs/cart/state/src/effects/cart-order.effects.ts @@ -41,26 +41,26 @@ import { DaffCartPlaceOrderFailure, DaffCartStorageFailure, DaffCartCreate, + DaffCartOrderActions, } from '../actions/public_api'; import { DAFF_CART_ERROR_MATCHER } from '../injection-tokens/public_api'; @Injectable() export class DaffCartOrderEffects< T extends DaffCart = DaffCart, - V extends DaffCartPaymentMethod = DaffCartPaymentMethod, R extends DaffCartOrderResult = DaffCartOrderResult > { constructor( - private actions$: Actions, + private actions$: Actions, @Inject(DAFF_CART_ERROR_MATCHER) private errorMatcher: ErrorTransformer, - @Inject(DaffCartOrderDriver) private driver: DaffCartOrderServiceInterface, + @Inject(DaffCartOrderDriver) private driver: DaffCartOrderServiceInterface, private storage: DaffCartStorageService, ) {} placeOrder$ = createEffect(() => this.actions$.pipe( ofType(DaffCartOrderActionTypes.CartPlaceOrderAction), - switchMap((action: DaffCartPlaceOrder) => defer(() => of(this.storage.getCartId())).pipe( + switchMap((action) => defer(() => of(this.storage.getCartId())).pipe( switchMap(cartId => this.driver.placeOrder(cartId, action.payload)), map((resp: R) => new DaffCartPlaceOrderSuccess(resp)), catchAndArrayifyErrors(error => of(error.find((err) => err.code === DAFF_STORAGE_SERVICE_ERROR_CODE) diff --git a/libs/cart/state/src/effects/cart-payment-processor.effects.ts b/libs/cart/state/src/effects/cart-payment-processor.effects.ts index 7fd803c6ca..390c540ecb 100644 --- a/libs/cart/state/src/effects/cart-payment-processor.effects.ts +++ b/libs/cart/state/src/effects/cart-payment-processor.effects.ts @@ -56,7 +56,7 @@ export class DaffCartPaymentProcessorEffects< @Inject(DAFF_PAYMENT_PROCESSOR_COLLECTION) private processors: DaffPaymentProcessorCollection, @Inject(DAFF_CART_ERROR_MATCHER) private errorMatcher: ErrorTransformer, @Inject(DAFF_PAYMENT_ERROR_MATCHER) private paymentErrorMatcher: ErrorTransformer, - @Inject(DaffCartPaymentDriver) private driver: DaffCartPaymentServiceInterface, + @Inject(DaffCartPaymentDriver) private driver: DaffCartPaymentServiceInterface, private storage: DaffCartStorageService, private injector: Injector, ) {} diff --git a/libs/cart/state/src/effects/cart-payment.effects.spec.ts b/libs/cart/state/src/effects/cart-payment.effects.spec.ts index 6ac9e167e6..9e59a4c2f3 100644 --- a/libs/cart/state/src/effects/cart-payment.effects.spec.ts +++ b/libs/cart/state/src/effects/cart-payment.effects.spec.ts @@ -60,7 +60,6 @@ describe('@daffodil/cart/state | DaffCartPaymentEffects', () => { let driverGetSpy: jasmine.Spy; let driverUpdateSpy: jasmine.Spy; - let driverUpdateWithBillingSpy: jasmine.Spy; let driverRemoveSpy: jasmine.Spy; let getCartIdSpy: jasmine.Spy; @@ -90,7 +89,6 @@ describe('@daffodil/cart/state | DaffCartPaymentEffects', () => { driverGetSpy = spyOn(daffPaymentDriver, 'get'); driverUpdateSpy = spyOn(daffPaymentDriver, 'update'); - driverUpdateWithBillingSpy = spyOn(daffPaymentDriver, 'updateWithBilling'); driverRemoveSpy = spyOn(daffPaymentDriver, 'remove'); getCartIdSpy = spyOn(daffCartStorageService, 'getCartId'); getCartIdSpy.and.returnValue(mockCart.id); @@ -184,7 +182,7 @@ describe('@daffodil/cart/state | DaffCartPaymentEffects', () => { describe('and the call to CartPaymentService is successful', () => { beforeEach(() => { - driverUpdateWithBillingSpy.and.returnValue(of(mockCart)); + driverUpdateSpy.and.returnValue(of(mockCart)); const cartPaymentUpdateSuccessAction = new DaffCartPaymentUpdateWithBillingSuccess(mockCart); actions$ = hot('--a', { a: cartPaymentUpdateAction }); expected = cold('--b', { b: cartPaymentUpdateSuccessAction }); @@ -199,7 +197,7 @@ describe('@daffodil/cart/state | DaffCartPaymentEffects', () => { beforeEach(() => { const error: DaffStateError = { code: 'code', recoverable: false, message: 'Failed to update cart payment and billing address' }; const response = cold('#', {}, error); - driverUpdateWithBillingSpy.and.returnValue(response); + driverUpdateSpy.and.returnValue(response); const cartPaymentUpdateFailureAction = new DaffCartPaymentUpdateWithBillingFailure([error]); actions$ = hot('--a', { a: cartPaymentUpdateAction }); expected = cold('--b', { b: cartPaymentUpdateFailureAction }); diff --git a/libs/cart/state/src/effects/cart-payment.effects.ts b/libs/cart/state/src/effects/cart-payment.effects.ts index 148322ecb5..c8ce270c6b 100644 --- a/libs/cart/state/src/effects/cart-payment.effects.ts +++ b/libs/cart/state/src/effects/cart-payment.effects.ts @@ -11,13 +11,10 @@ import { of } from 'rxjs'; import { switchMap, map, - catchError, } from 'rxjs/operators'; import { - DaffCartPaymentMethod, DaffCart, - DaffCartAddress, DaffCartStorageService, } from '@daffodil/cart'; import { @@ -29,40 +26,33 @@ import { ErrorTransformer } from '@daffodil/core/state'; import { DaffCartPaymentActionTypes, - DaffCartPaymentLoad, DaffCartPaymentLoadSuccess, DaffCartPaymentLoadFailure, - DaffCartPaymentRemove, DaffCartPaymentRemoveSuccess, DaffCartPaymentRemoveFailure, - DaffCartPaymentUpdate, DaffCartPaymentUpdateSuccess, DaffCartPaymentUpdateFailure, - DaffCartPaymentUpdateWithBilling, DaffCartPaymentUpdateWithBillingSuccess, DaffCartPaymentUpdateWithBillingFailure, + DaffCartPaymentActions, } from '../actions/public_api'; import { DAFF_CART_ERROR_MATCHER } from '../injection-tokens/public_api'; @Injectable() -export class DaffCartPaymentEffects< - T extends DaffCartPaymentMethod = DaffCartPaymentMethod, - V extends DaffCart = DaffCart, - R extends DaffCartAddress = DaffCartAddress, -> { +export class DaffCartPaymentEffects { constructor( - private actions$: Actions, + private actions$: Actions>, @Inject(DAFF_CART_ERROR_MATCHER) private errorMatcher: ErrorTransformer, - @Inject(DaffCartPaymentDriver) private driver: DaffCartPaymentServiceInterface, + @Inject(DaffCartPaymentDriver) private driver: DaffCartPaymentServiceInterface, private storage: DaffCartStorageService, ) {} get$ = createEffect(() => this.actions$.pipe( ofType(DaffCartPaymentActionTypes.CartPaymentLoadAction), - switchMap((action: DaffCartPaymentLoad) => + switchMap((action) => this.driver.get(this.storage.getCartId()).pipe( - map((resp: T) => new DaffCartPaymentLoadSuccess(resp)), + map((resp) => new DaffCartPaymentLoadSuccess(resp)), catchAndArrayifyErrors(error => of(new DaffCartPaymentLoadFailure(error.map(this.errorMatcher)))), ), ), @@ -71,9 +61,9 @@ export class DaffCartPaymentEffects< update$ = createEffect(() => this.actions$.pipe( ofType(DaffCartPaymentActionTypes.CartPaymentUpdateAction), - switchMap((action: DaffCartPaymentUpdate) => + switchMap((action) => this.driver.update(this.storage.getCartId(), action.payload).pipe( - map((resp: V) => new DaffCartPaymentUpdateSuccess(resp)), + map((resp) => new DaffCartPaymentUpdateSuccess(resp)), catchAndArrayifyErrors(error => of(new DaffCartPaymentUpdateFailure(error.map(this.errorMatcher)))), ), ), @@ -82,8 +72,8 @@ export class DaffCartPaymentEffects< updateWithBilling$ = createEffect(() => this.actions$.pipe( ofType(DaffCartPaymentActionTypes.CartPaymentUpdateWithBillingAction), - switchMap((action: DaffCartPaymentUpdateWithBilling) => - this.driver.updateWithBilling(this.storage.getCartId(), action.payment, action.address).pipe( + switchMap((action) => + this.driver.update(this.storage.getCartId(), action.payment, action.address).pipe( map(resp => new DaffCartPaymentUpdateWithBillingSuccess(resp)), catchAndArrayifyErrors(error => of(new DaffCartPaymentUpdateWithBillingFailure(error.map(this.errorMatcher)))), ), @@ -93,7 +83,7 @@ export class DaffCartPaymentEffects< remove$ = createEffect(() => this.actions$.pipe( ofType(DaffCartPaymentActionTypes.CartPaymentRemoveAction), - switchMap((action: DaffCartPaymentRemove) => + switchMap((action) => this.driver.remove(this.storage.getCartId()).pipe( map(() => new DaffCartPaymentRemoveSuccess()), catchAndArrayifyErrors(error => of(new DaffCartPaymentRemoveFailure(error.map(this.errorMatcher)))), diff --git a/libs/cart/state/src/effects/cart-shipping-address.effects.spec.ts b/libs/cart/state/src/effects/cart-shipping-address.effects.spec.ts index cb29cabd6f..7ff3ec2873 100644 --- a/libs/cart/state/src/effects/cart-shipping-address.effects.spec.ts +++ b/libs/cart/state/src/effects/cart-shipping-address.effects.spec.ts @@ -37,7 +37,7 @@ import { DaffCartShippingAddressEffects } from './cart-shipping-address.effects' describe('@daffodil/cart/state | DaffCartShippingAddressEffects', () => { let actions$: Observable; - let effects: DaffCartShippingAddressEffects; + let effects: DaffCartShippingAddressEffects; let mockCart: DaffCart; let mockCartShippingAddress: DaffCartAddress; diff --git a/libs/cart/state/src/effects/cart-shipping-address.effects.ts b/libs/cart/state/src/effects/cart-shipping-address.effects.ts index 9038cea885..c6855143ee 100644 --- a/libs/cart/state/src/effects/cart-shipping-address.effects.ts +++ b/libs/cart/state/src/effects/cart-shipping-address.effects.ts @@ -11,11 +11,9 @@ import { of } from 'rxjs'; import { switchMap, map, - catchError, } from 'rxjs/operators'; import { - DaffCartAddress, DaffCart, DaffCartStorageService, } from '@daffodil/cart'; @@ -28,30 +26,29 @@ import { ErrorTransformer } from '@daffodil/core/state'; import { DaffCartShippingAddressActionTypes, - DaffCartShippingAddressLoad, DaffCartShippingAddressLoadSuccess, DaffCartShippingAddressLoadFailure, - DaffCartShippingAddressUpdate, DaffCartShippingAddressUpdateSuccess, DaffCartShippingAddressUpdateFailure, + DaffCartShippingAddressActions, } from '../actions/public_api'; import { DAFF_CART_ERROR_MATCHER } from '../injection-tokens/public_api'; @Injectable() -export class DaffCartShippingAddressEffects { +export class DaffCartShippingAddressEffects { constructor( - private actions$: Actions, + private actions$: Actions>, @Inject(DAFF_CART_ERROR_MATCHER) private errorMatcher: ErrorTransformer, - @Inject(DaffCartShippingAddressDriver) private driver: DaffCartShippingAddressServiceInterface, + @Inject(DaffCartShippingAddressDriver) private driver: DaffCartShippingAddressServiceInterface, private storage: DaffCartStorageService, ) {} get$ = createEffect(() => this.actions$.pipe( ofType(DaffCartShippingAddressActionTypes.CartShippingAddressLoadAction), - switchMap((action: DaffCartShippingAddressLoad) => + switchMap((action) => this.driver.get(this.storage.getCartId()).pipe( - map((resp: T) => new DaffCartShippingAddressLoadSuccess(resp)), + map((resp) => new DaffCartShippingAddressLoadSuccess(resp)), catchAndArrayifyErrors(error => of(new DaffCartShippingAddressLoadFailure(error.map(this.errorMatcher)))), ), ), @@ -60,9 +57,9 @@ export class DaffCartShippingAddressEffects this.actions$.pipe( ofType(DaffCartShippingAddressActionTypes.CartShippingAddressUpdateAction), - switchMap((action: DaffCartShippingAddressUpdate) => + switchMap((action) => this.driver.update(this.storage.getCartId(), action.payload).pipe( - map((resp: V) => new DaffCartShippingAddressUpdateSuccess(resp)), + map((resp) => new DaffCartShippingAddressUpdateSuccess(resp)), catchAndArrayifyErrors(error => of(new DaffCartShippingAddressUpdateFailure(error.map(this.errorMatcher)))), ), ), diff --git a/libs/cart/state/src/effects/cart-shipping-information.effects.ts b/libs/cart/state/src/effects/cart-shipping-information.effects.ts index f16ae32be9..72daf79907 100644 --- a/libs/cart/state/src/effects/cart-shipping-information.effects.ts +++ b/libs/cart/state/src/effects/cart-shipping-information.effects.ts @@ -39,9 +39,9 @@ import { DAFF_CART_ERROR_MATCHER } from '../injection-tokens/public_api'; @Injectable() export class DaffCartShippingInformationEffects { constructor( - private actions$: Actions>, + private actions$: Actions>, @Inject(DAFF_CART_ERROR_MATCHER) private errorMatcher: ErrorTransformer, - @Inject(DaffCartShippingInformationDriver) private driver: DaffCartShippingInformationServiceInterface, + @Inject(DaffCartShippingInformationDriver) private driver: DaffCartShippingInformationServiceInterface, private storage: DaffCartStorageService, ) {} diff --git a/libs/cart/state/src/effects/cart.effects.ts b/libs/cart/state/src/effects/cart.effects.ts index 7e5b4c27f7..c11375c520 100644 --- a/libs/cart/state/src/effects/cart.effects.ts +++ b/libs/cart/state/src/effects/cart.effects.ts @@ -54,7 +54,7 @@ import { import { DAFF_CART_ERROR_MATCHER } from '../injection-tokens/public_api'; @Injectable() -export class DaffCartEffects { +export class DaffCartEffects { constructor( private actions$: Actions, @Inject(DAFF_CART_ERROR_MATCHER) private errorMatcher: ErrorTransformer, diff --git a/libs/cart/state/src/facades/cart/cart-facade.interface.ts b/libs/cart/state/src/facades/cart/cart-facade.interface.ts index 1f74e89b6a..8574e743cc 100644 --- a/libs/cart/state/src/facades/cart/cart-facade.interface.ts +++ b/libs/cart/state/src/facades/cart/cart-facade.interface.ts @@ -11,14 +11,12 @@ import { DaffCartItemDiscount, } from '@daffodil/cart'; import { + DaffOperationEntity, + DaffState, DaffStateError, DaffStoreFacade, } from '@daffodil/core/state'; -import { - DaffCartItemStateEnum, - DaffStatefulCartItem, -} from '../../models/stateful-cart-item'; import { DaffCartOperationType } from '../../reducers/cart-operation-type.enum'; import { DaffCartErrors } from '../../reducers/errors/cart-errors.type'; import { DaffCartLoading } from '../../reducers/loading/cart-loading.type'; @@ -27,7 +25,6 @@ import { DaffCartResolveState } from '../../reducers/public_api'; export interface DaffCartFacadeInterface< T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem > extends DaffStoreFacade { cart$: Observable; @@ -189,25 +186,10 @@ export interface DaffCartFacadeInterface< paymentMethodsErrors$: Observable; couponErrors$: Observable; - id$: Observable; - subtotal$: Observable; - grandTotal$: Observable; - subtotalExcludingTax$: Observable; - subtotalIncludingTax$: Observable; - subtotalWithDiscountExcludingTax$: Observable; - subtotalWithDiscountIncludingTax$: Observable; - discountTotals$: Observable; - totalTax$: Observable; - shippingTotal$: Observable; - coupons$: Observable; - /** - * @deprecated use `itemEntities$` instead - */ - items$: Observable; /** * A list of the cart items. */ - itemEntities$: Observable; + itemEntities$: Observable[]>; /** * The total number of cart items, taking into account the quantity of each cart item. */ @@ -216,19 +198,12 @@ export interface DaffCartFacadeInterface< /** * All cart items that are out of stock. */ - outOfStockItems$: Observable; + outOfStockItems$: Observable[]>; /** * All cart items that are in stock. */ - inStockItems$: Observable; - itemDictionary$: Observable>; - billingAddress$: Observable; - shippingAddress$: Observable; - payment$: Observable; - totals$: Observable; - shippingInformation$: Observable; - availableShippingMethods$: Observable; - availablePaymentMethods$: Observable; + inStockItems$: Observable[]>; + itemDictionary$: Observable>>; /** * The user-defined platform-agnostic payment identifier that corresponds to the cart's current (platform-specific) payment method. * Define the mapping with the `DaffCartPaymentMethodIdMap` injection token. @@ -257,43 +232,7 @@ export interface DaffCartFacadeInterface< orderResultCartId$: Observable; hasOrderResult$: Observable; - getConfiguredCartItemAttributes(itemId: U['id']): Observable; - getCompositeCartItemOptions(itemId: U['id']): Observable; - isCartItemOutOfStock(itemId: U['id']): Observable; - /** - * The state of a cart item. - */ - getCartItemState(itemId: U['id']): Observable; - /** - * Selects the specified item's price. - * Zero by default. - * This includes any discounts and sales that apply to the product or category. - * This excludes cart discounts. - */ - getCartItemPrice(itemId: U['id']): Observable; - /** - * Selects the specified item's quantity. - * Zero by default. - */ - getCartItemQuantity(itemId: U['id']): Observable; - /** - * Selects the specified item's row total. - * Zero by default. - * This includes any discounts and sales that apply to the product or category. - * This excludes cart discounts. - */ - getCartItemRowTotal(itemId: U['id']): Observable; - /** - * Selects the specified item's array of cart (not product) discounts. - */ - getCartItemDiscounts(itemId: U['id']): Observable; - /** - * Selects the specified item's sum of all cart (not product) discounts for the entire row. - * Zero by default. - */ - getCartItemTotalDiscount(itemId: U['id']): Observable; - /** - * Gets the specified item's errors. - */ - getCartItemErrors(itemId: U['id']): Observable; + getConfiguredCartItemAttributes(itemId: T['items'][number]['id']): Observable; + getCompositeCartItemOptions(itemId: T['items'][number]['id']): Observable; + isCartItemOutOfStock(itemId: T['items'][number]['id']): Observable; } diff --git a/libs/cart/state/src/facades/cart/cart.facade.spec.ts b/libs/cart/state/src/facades/cart/cart.facade.spec.ts index 4930985672..4a4f7bf581 100644 --- a/libs/cart/state/src/facades/cart/cart.facade.spec.ts +++ b/libs/cart/state/src/facades/cart/cart.facade.spec.ts @@ -68,6 +68,7 @@ import { daffCartRetrivalActions, daffCartItemEntitiesRetrievalActionsReducerFactory, daffCartRetrievalActionsReducerFactory, + DaffCartItemUpdate, } from '@daffodil/cart/state'; import { DaffStatefulCartItemFactory } from '@daffodil/cart/state/testing'; import { @@ -394,7 +395,9 @@ describe('DaffCartFacade', () => { describe('when the cart item mutations have not completed', () => { beforeEach(() => { - facade.dispatch(new DaffCartItemDelete('itemId')); + const mockCartItems = statefulCartItemFactory.createMany(2); + store.dispatch(new DaffCartItemListSuccess(mockCartItems)); + store.dispatch(new DaffCartItemUpdate(mockCartItems[0].id, { qty: 2 })); }); it('should return true', () => { @@ -949,146 +952,6 @@ describe('DaffCartFacade', () => { }); }); - describe('id$', () => { - it('should initially be null', () => { - const expected = cold('a', { a: null }); - expect(facade.id$).toBeObservable(expected); - }); - - it('should be the cart id upon a successful cart creation', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.id }); - facade.dispatch(new DaffCartCreateSuccess(cart)); - expect(facade.id$).toBeObservable(expected); - }); - }); - - describe('subtotal$', () => { - it('should initially be null', () => { - const expected = cold('a', { a: null }); - expect(facade.subtotal$).toBeObservable(expected); - }); - - it('should be the cart subtotal upon a successful cart load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.subtotalExcludingTax].value }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.subtotal$).toBeObservable(expected); - }); - }); - - describe('grandTotal$', () => { - it('should initially be null', () => { - const expected = cold('a', { a: null }); - expect(facade.grandTotal$).toBeObservable(expected); - }); - - it('should be the cart grand total upon a successful cart load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.grandTotal].value }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.grandTotal$).toBeObservable(expected); - }); - }); - - describe('subtotalExcludingTax$', () => { - - it('should be the cart subtotal excluding tax upon a successful cart load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.subtotalExcludingTax].value }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.subtotalExcludingTax$).toBeObservable(expected); - }); - }); - - describe('subtotalIncludingTax$', () => { - - it('should be the cart subtotal including tax upon a successful cart load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.subtotalIncludingTax].value }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.subtotalIncludingTax$).toBeObservable(expected); - }); - }); - - describe('subtotalWithDiscountExcludingTax$', () => { - - it('should be the cart subtotal with discounts excluding tax upon a successful cart load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.subtotalWithDiscountExcludingTax].value }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.subtotalWithDiscountExcludingTax$).toBeObservable(expected); - }); - }); - - describe('subtotalWithDiscountIncludingTax$', () => { - - it('should be the cart subtotal with discounts including tax upon a successful cart load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.subtotalWithDiscountIncludingTax].value }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.subtotalWithDiscountIncludingTax$).toBeObservable(expected); - }); - }); - - describe('discountTotals$', () => { - - it('should be the cart discount totals upon a successful cart load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: [cart.totals[DaffCartTotalTypeEnum.discount]]}); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.discountTotals$).toBeObservable(expected); - }); - }); - - describe('totalTax$', () => { - - it('should be the cart tax total upon a successful cart load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.tax].value }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.totalTax$).toBeObservable(expected); - }); - }); - - describe('shippingTotal$', () => { - - it('should be the cart shipping total upon a successful cart load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.shipping].value }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.shippingTotal$).toBeObservable(expected); - }); - }); - - describe('coupons$', () => { - it('should initially be an empty array', () => { - const expected = cold('a', { a: []}); - expect(facade.coupons$).toBeObservable(expected); - }); - - it('should be the cart coupons upon a successful cart load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.coupons }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.coupons$).toBeObservable(expected); - }); - }); - - describe('items$', () => { - it('should initially be an empty array', () => { - const expected = cold('a', { a: []}); - expect(facade.items$).toBeObservable(expected); - }); - - it('should be the cart items upon a successful cart item list', () => { - const statefulCartItems = statefulCartItemFactory.createMany(2); - const expected = cold('a', { a: statefulCartItems }); - facade.dispatch(new DaffCartItemListSuccess(statefulCartItems)); - expect(facade.items$).toBeObservable(expected); - }); - }); - describe('itemEntities$', () => { it('should initially be an empty array', () => { const expected = cold('a', { a: []}); @@ -1096,7 +959,7 @@ describe('DaffCartFacade', () => { }); it('should be the cart items upon a successful cart item list', () => { - const statefulCartItems = statefulCartItemFactory.createMany(2); + const statefulCartItems = statefulCartItemFactory.createMany(2, { daffState: jasmine.anything() }); const expected = cold('a', { a: statefulCartItems }); facade.dispatch(new DaffCartItemListSuccess(statefulCartItems)); expect(facade.itemEntities$).toBeObservable(expected); @@ -1110,7 +973,7 @@ describe('DaffCartFacade', () => { }); it('should be the total number of cart items upon a successful cart item list', () => { - const statefulCartItems = statefulCartItemFactory.createMany(2); + const statefulCartItems = statefulCartItemFactory.createMany(2, { daffState: jasmine.anything() }); const expected = cold('a', { a: statefulCartItems.reduce((acc, item) => acc + item.qty, 0) }); facade.dispatch(new DaffCartItemListSuccess(statefulCartItems)); expect(facade.totalItems$).toBeObservable(expected); @@ -1120,7 +983,7 @@ describe('DaffCartFacade', () => { describe('hasOutOfStockItems$', () => { it('should return whether or not the cart has out of stock items', () => { - const statefulCartItems = statefulCartItemFactory.createMany(2); + const statefulCartItems = statefulCartItemFactory.createMany(2, { daffState: jasmine.anything() }); const expected = cold('a', { a: false }); facade.dispatch(new DaffCartItemListSuccess(statefulCartItems)); expect(facade.hasOutOfStockItems$).toBeObservable(expected); @@ -1131,6 +994,7 @@ describe('DaffCartFacade', () => { it('should return out of stock items', () => { const statefulCartItems = statefulCartItemFactory.createMany(2, { in_stock: false, + daffState: jasmine.anything(), }); const expected = cold('a', { a: statefulCartItems }); facade.dispatch(new DaffCartItemListSuccess(statefulCartItems)); @@ -1142,6 +1006,7 @@ describe('DaffCartFacade', () => { it('should return in stock items', () => { const statefulCartItems = statefulCartItemFactory.createMany(2, { in_stock: true, + daffState: jasmine.anything(), }); const expected = cold('a', { a: statefulCartItems }); facade.dispatch(new DaffCartItemListSuccess(statefulCartItems)); @@ -1156,7 +1021,7 @@ describe('DaffCartFacade', () => { }); it('should be the cart items upon a successful cart item list', () => { - const statefulCartItems = statefulCartItemFactory.createMany(2); + const statefulCartItems = statefulCartItemFactory.createMany(2, { daffState: jasmine.anything() }); const expected = cold('a', { a: statefulCartItems.reduce((acc, item) => ({ @@ -1169,106 +1034,6 @@ describe('DaffCartFacade', () => { }); }); - describe('billingAddress$', () => { - it('should initially be null', () => { - const expected = cold('a', { a: null }); - expect(facade.billingAddress$).toBeObservable(expected); - }); - - it('should be the cart billing address upon a successful cart billing address load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.billing_address }); - facade.dispatch(new DaffCartBillingAddressLoadSuccess(cart.billing_address)); - expect(facade.billingAddress$).toBeObservable(expected); - }); - }); - - describe('shippingAddress$', () => { - it('should initially be null', () => { - const expected = cold('a', { a: null }); - expect(facade.shippingAddress$).toBeObservable(expected); - }); - - it('should be the cart shipping address upon a successful cart shipping address load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.shipping_address }); - facade.dispatch(new DaffCartShippingAddressLoadSuccess(cart.shipping_address)); - expect(facade.shippingAddress$).toBeObservable(expected); - }); - }); - - describe('payment$', () => { - it('should initially be null', () => { - const expected = cold('a', { a: null }); - expect(facade.payment$).toBeObservable(expected); - }); - - it('should be the cart payment upon a successful cart payment load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.payment }); - facade.dispatch(new DaffCartPaymentLoadSuccess(cart.payment)); - expect(facade.payment$).toBeObservable(expected); - }); - }); - - describe('totals$', () => { - it('should initially be an empty array', () => { - const expected = cold('a', { a: []}); - expect(facade.totals$).toBeObservable(expected); - }); - - it('should be the cart totals upon a successful cart item list', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.totals }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.totals$).toBeObservable(expected); - }); - }); - - describe('shippingInformation$', () => { - it('should initially be null', () => { - const expected = cold('a', { a: null }); - expect(facade.shippingInformation$).toBeObservable(expected); - }); - - it('should be the cart shipping information upon a successful cart shipping information load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { - a: cart.shipping_information, - }); - facade.dispatch(new DaffCartShippingInformationLoadSuccess(cart.shipping_information)); - expect(facade.shippingInformation$).toBeObservable(expected); - }); - }); - - describe('availableShippingMethods$', () => { - it('should initially be an empty array', () => { - const expected = cold('a', { a: []}); - expect(facade.availableShippingMethods$).toBeObservable(expected); - }); - - it('should be the cart available shipping methods upon a successful available shipping methods load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.available_shipping_methods }); - facade.dispatch(new DaffCartShippingMethodsLoadSuccess(cart.available_shipping_methods)); - expect(facade.availableShippingMethods$).toBeObservable(expected); - }); - }); - - describe('availablePaymentMethods$', () => { - it('should initially be an empty array', () => { - const expected = cold('a', { a: []}); - expect(facade.availablePaymentMethods$).toBeObservable(expected); - }); - - it('should be the cart available payment methods upon a successful available payment methods load', () => { - const cart = cartFactory.create(); - const expected = cold('a', { a: cart.available_payment_methods }); - facade.dispatch(new DaffCartPaymentMethodsLoadSuccess(cart.available_payment_methods)); - expect(facade.availablePaymentMethods$).toBeObservable(expected); - }); - }); - describe('paymentId$', () => { let mockPayment: DaffCartPaymentMethod; let cart: DaffCart; @@ -1558,7 +1323,7 @@ describe('DaffCartFacade', () => { it('should return whether the cart item is out of stock', () => { const cart = cartFactory.create({ - items: statefulCartItemFactory.createMany(2), + items: statefulCartItemFactory.createMany(2, { daffState: jasmine.anything() }), }); const expected = cold('a', { a: !cart.items[0].in_stock }); facade.dispatch(new DaffCartLoadSuccess(cart)); @@ -1566,19 +1331,6 @@ describe('DaffCartFacade', () => { }); }); - describe('getCartItemState', () => { - - it('should return the cart item state', () => { - const statefulCartItem = statefulCartItemFactory.create(); - const cart = cartFactory.create({ - items: [statefulCartItem], - }); - const expected = cold('a', { a: statefulCartItem.daffState }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.getCartItemState(statefulCartItem.id)).toBeObservable(expected); - }); - }); - describe('hasBillingAddress$', () => { describe('when all the billing address is present', () => { beforeEach(() => { @@ -1687,85 +1439,4 @@ describe('DaffCartFacade', () => { expect(facade.canPlaceOrder$).toBeObservable(expected); }); }); - - describe('getCartItemPrice', () => { - - it('should be the cart item\'s price', () => { - const cart = cartFactory.create({ - items: statefulCartItemFactory.createMany(2), - }); - const expected = cold('a', { a: cart.items[0].price }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.getCartItemPrice(cart.items[0].id)).toBeObservable(expected); - }); - }); - - describe('getCartItemQuantity', () => { - - it('should be the cart item\'s quantity', () => { - const cart = cartFactory.create({ - items: statefulCartItemFactory.createMany(2), - }); - const expected = cold('a', { a: cart.items[0].qty }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.getCartItemQuantity(cart.items[0].id)).toBeObservable(expected); - }); - }); - - describe('getCartItemRowTotal', () => { - - it('should be the cart item\'s row total', () => { - const cart = cartFactory.create({ - items: statefulCartItemFactory.createMany(2), - }); - const expected = cold('a', { a: cart.items[0].row_total }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.getCartItemRowTotal(cart.items[0].id)).toBeObservable(expected); - }); - }); - - describe('getCartItemDiscounts', () => { - - it('should be the cart item\'s array of discounts', () => { - const cart = cartFactory.create({ - items: statefulCartItemFactory.createMany(2), - }); - const expected = cold('a', { a: cart.items[0].discounts }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.getCartItemDiscounts(cart.items[0].id)).toBeObservable(expected); - }); - }); - - describe('getCartItemTotalDiscount', () => { - - it('should be the cart item\'s sum of all discounts', () => { - const cart = cartFactory.create({ - items: statefulCartItemFactory.createMany(2), - }); - const expected = cold('a', { a: cart.items[0].discounts.reduce((acc, { amount }) => acc + amount, 0) }); - facade.dispatch(new DaffCartLoadSuccess(cart)); - expect(facade.getCartItemTotalDiscount(cart.items[0].id)).toBeObservable(expected); - }); - }); - - describe('getCartItemErrors', () => { - let error: DaffStateError; - - beforeEach(() => { - error = { - code: 'code', - message: 'message', - }; - }); - - it('should be the cart item\'s sum of all discounts', () => { - const cart = cartFactory.create({ - items: statefulCartItemFactory.createMany(2), - }); - const expected = cold('a', { a: [error]}); - facade.dispatch(new DaffCartLoadSuccess(cart)); - facade.dispatch(new DaffCartItemUpdateFailure([error], cart.items[0].id)); - expect(facade.getCartItemErrors(cart.items[0].id)).toBeObservable(expected); - }); - }); }); diff --git a/libs/cart/state/src/facades/cart/cart.facade.ts b/libs/cart/state/src/facades/cart/cart.facade.ts index a1082d1861..6ef8aa8cad 100644 --- a/libs/cart/state/src/facades/cart/cart.facade.ts +++ b/libs/cart/state/src/facades/cart/cart.facade.ts @@ -20,13 +20,13 @@ import { DaffCompositeCartItemOption, DaffCartItemDiscount, } from '@daffodil/cart'; -import { DaffStateError } from '@daffodil/core/state'; +import { + DaffOperationEntity, + DaffState, + DaffStateError, +} from '@daffodil/core/state'; import { DaffCartFacadeInterface } from './cart-facade.interface'; -import { - DaffCartItemStateEnum, - DaffStatefulCartItem, -} from '../../models/stateful-cart-item'; import { DaffCartOperationType } from '../../reducers/cart-operation-type.enum'; import { DaffCartErrors } from '../../reducers/errors/cart-errors.type'; import { DaffCartLoading } from '../../reducers/loading/cart-loading.type'; @@ -48,8 +48,7 @@ import { export class DaffCartFacade< T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem -> implements DaffCartFacadeInterface { +> implements DaffCartFacadeInterface { cart$: Observable; resolved$: Observable; @@ -96,31 +95,12 @@ export class DaffCartFacade< paymentMethodsErrors$: Observable; couponErrors$: Observable; - id$: Observable; - subtotal$: Observable; - grandTotal$: Observable; - subtotalExcludingTax$: Observable; - subtotalIncludingTax$: Observable; - subtotalWithDiscountExcludingTax$: Observable; - subtotalWithDiscountIncludingTax$: Observable; - discountTotals$: Observable; - totalTax$: Observable; - shippingTotal$: Observable; - coupons$: Observable; - itemEntities$: Observable; - items$: Observable; + itemEntities$: Observable[]>; totalItems$: Observable; hasOutOfStockItems$: Observable; - outOfStockItems$: Observable; - inStockItems$: Observable; - itemDictionary$: Observable>; - billingAddress$: Observable; - shippingAddress$: Observable; - payment$: Observable; - totals$: Observable; - shippingInformation$: Observable; - availableShippingMethods$: Observable; - availablePaymentMethods$: Observable; + outOfStockItems$: Observable[]>; + inStockItems$: Observable[]>; + itemDictionary$: Observable>>; paymentId$: Observable; isCartEmpty$: Observable; @@ -139,19 +119,12 @@ export class DaffCartFacade< orderResultCartId$: Observable; hasOrderResult$: Observable; - private _selectCartItemConfiguredAttributes: DaffCartMemoizedSelectors['selectCartItemConfiguredAttributes']; - private _selectCartItemCompositeOptions: DaffCartMemoizedSelectors['selectCartItemCompositeOptions']; - private _selectIsCartItemOutOfStock: DaffCartMemoizedSelectors['selectIsCartItemOutOfStock']; - private _selectCartItemState: DaffCartMemoizedSelectors['selectCartItemState']; - private _selectCartItemPrice: DaffCartMemoizedSelectors['selectCartItemPrice']; - private _selectCartItemQuantity: DaffCartMemoizedSelectors['selectCartItemQuantity']; - private _selectCartItemRowTotal: DaffCartMemoizedSelectors['selectCartItemRowTotal']; - private _selectCartItemDiscounts: DaffCartMemoizedSelectors['selectCartItemDiscounts']; - private _selectCartItemTotalDiscount: DaffCartMemoizedSelectors['selectCartItemTotalDiscount']; - private _selectCartItemErrors: DaffCartMemoizedSelectors['selectCartItemErrors']; + private _selectCartItemConfiguredAttributes: DaffCartMemoizedSelectors['selectCartItemConfiguredAttributes']; + private _selectCartItemCompositeOptions: DaffCartMemoizedSelectors['selectCartItemCompositeOptions']; + private _selectIsCartItemOutOfStock: DaffCartMemoizedSelectors['selectIsCartItemOutOfStock']; constructor( - private store: Store>, + private store: Store>, // typing this as `Record` or `object` fails the build // because Angular explicitly types this as `Object` // eslint-disable-next-line @typescript-eslint/ban-types @@ -204,18 +177,6 @@ export class DaffCartFacade< selectPaymentMethodsErrors, selectCouponErrors, - selectCartId, - selectCartSubtotal, - selectCartGrandTotal, - selectCartSubtotalExcludingTax, - selectCartSubtotalIncludingTax, - selectCartSubtotalWithDiscountExcludingTax, - selectCartSubtotalWithDiscountIncludingTax, - selectCartDiscountTotals, - selectCartTotalTax, - selectCartShippingTotal, - selectCartCoupons, - selectCartItems, selectAllCartItems, selectCartHasOutOfStockItems, selectOutOfStockCartItems, @@ -224,13 +185,6 @@ export class DaffCartFacade< selectTotalNumberOfCartItems, selectCartItemConfiguredAttributes, selectCartItemCompositeOptions, - selectCartBillingAddress, - selectCartShippingAddress, - selectCartPayment, - selectCartTotals, - selectCartShippingInformation, - selectCartAvailableShippingMethods, - selectCartAvailablePaymentMethods, selectIsCartEmpty, selectIsBillingSameAsShipping, @@ -242,31 +196,16 @@ export class DaffCartFacade< selectCartOrderCartId, selectHasOrderResult, selectIsCartItemOutOfStock, - selectCartItemState, selectHasBillingAddress, selectHasShippingAddress, selectHasShippingMethod, selectHasPaymentMethod, selectCanPlaceOrder, - - selectCartItemPrice, - selectCartItemRowTotal, - selectCartItemQuantity, - selectCartItemDiscounts, - selectCartItemTotalDiscount, - selectCartItemErrors, - } = getDaffCartSelectors(); + } = getDaffCartSelectors(); this._selectCartItemConfiguredAttributes = selectCartItemConfiguredAttributes; this._selectCartItemCompositeOptions = selectCartItemCompositeOptions; this._selectIsCartItemOutOfStock = selectIsCartItemOutOfStock; - this._selectCartItemState = selectCartItemState; - this._selectCartItemPrice = selectCartItemPrice; - this._selectCartItemQuantity = selectCartItemQuantity; - this._selectCartItemRowTotal = selectCartItemRowTotal; - this._selectCartItemDiscounts = selectCartItemDiscounts; - this._selectCartItemTotalDiscount = selectCartItemTotalDiscount; - this._selectCartItemErrors = selectCartItemErrors; this.cart$ = this.store.pipe(select(selectCartValue)); @@ -314,32 +253,14 @@ export class DaffCartFacade< this.paymentMethodsErrors$ = this.store.pipe(select(selectPaymentMethodsErrors)); this.couponErrors$ = this.store.pipe(select(selectCouponErrors)); - this.id$ = this.store.pipe(select(selectCartId)); - this.subtotal$ = this.store.pipe(select(selectCartSubtotal)); - this.grandTotal$ = this.store.pipe(select(selectCartGrandTotal)); - this.subtotalExcludingTax$ = this.store.pipe(select(selectCartSubtotalExcludingTax)); - this.subtotalIncludingTax$ = this.store.pipe(select(selectCartSubtotalIncludingTax)); - this.subtotalWithDiscountExcludingTax$ = this.store.pipe(select(selectCartSubtotalWithDiscountExcludingTax)); - this.subtotalWithDiscountIncludingTax$ = this.store.pipe(select(selectCartSubtotalWithDiscountIncludingTax)); - this.discountTotals$ = this.store.pipe(select(selectCartDiscountTotals)); - this.totalTax$ = this.store.pipe(select(selectCartTotalTax)); - this.shippingTotal$ = this.store.pipe(select(selectCartShippingTotal)); - this.coupons$ = this.store.pipe(select(selectCartCoupons)); - this.items$ = this.store.pipe(select(selectCartItems)); this.itemEntities$ = this.store.pipe(select(selectAllCartItems)); this.totalItems$ = this.store.pipe(select(selectTotalNumberOfCartItems)); this.hasOutOfStockItems$ = this.store.pipe(select(selectCartHasOutOfStockItems)); this.outOfStockItems$ = this.store.pipe(select(selectOutOfStockCartItems)); this.inStockItems$ = this.store.pipe(select(selectInStockCartItems)); this.itemDictionary$ = this.store.pipe(select(selectCartItemEntities)); - this.billingAddress$ = this.store.pipe(select(selectCartBillingAddress)); - this.shippingAddress$ = this.store.pipe(select(selectCartShippingAddress)); - this.payment$ = this.store.pipe(select(selectCartPayment)); - this.totals$ = this.store.pipe(select(selectCartTotals)); - this.shippingInformation$ = this.store.pipe(select(selectCartShippingInformation)); - this.availableShippingMethods$ = this.store.pipe(select(selectCartAvailableShippingMethods)); - this.availablePaymentMethods$ = this.store.pipe(select(selectCartAvailablePaymentMethods)); - this.paymentId$ = this.payment$.pipe( + this.paymentId$ = this.cart$.pipe( + map((cart) => cart?.payment), map(payment => payment && payment.method ? this.paymentMethodMap[payment.method] @@ -364,46 +285,18 @@ export class DaffCartFacade< this.hasOrderResult$ = this.store.pipe(select(selectHasOrderResult)); } - getConfiguredCartItemAttributes(itemId: U['id']): Observable { + getConfiguredCartItemAttributes(itemId: T['items'][number]['id']): Observable { return this.store.pipe(select(this._selectCartItemConfiguredAttributes(itemId))); }; - getCompositeCartItemOptions(itemId: U['id']): Observable { + getCompositeCartItemOptions(itemId: T['items'][number]['id']): Observable { return this.store.pipe(select(this._selectCartItemCompositeOptions(itemId))); }; - isCartItemOutOfStock(itemId: U['id']): Observable { + isCartItemOutOfStock(itemId: T['items'][number]['id']): Observable { return this.store.pipe(select(this._selectIsCartItemOutOfStock(itemId))); } - getCartItemState(itemId: U['id']): Observable { - return this.store.pipe(select(this._selectCartItemState(itemId))); - } - - getCartItemPrice(itemId: U['id']): Observable { - return this.store.pipe(select(this._selectCartItemPrice(itemId))); - } - - getCartItemQuantity(itemId: U['id']): Observable { - return this.store.pipe(select(this._selectCartItemQuantity(itemId))); - } - - getCartItemRowTotal(itemId: U['id']): Observable { - return this.store.pipe(select(this._selectCartItemRowTotal(itemId))); - } - - getCartItemDiscounts(itemId: U['id']): Observable { - return this.store.pipe(select(this._selectCartItemDiscounts(itemId))); - } - - getCartItemTotalDiscount(itemId: U['id']): Observable { - return this.store.pipe(select(this._selectCartItemTotalDiscount(itemId))); - } - - getCartItemErrors(itemId: U['id']): Observable { - return this.store.pipe(select(this._selectCartItemErrors(itemId))); - }; - dispatch(action: Action) { this.store.dispatch(action); } diff --git a/libs/cart/state/src/helpers/cart-items-entity-transform.spec.ts b/libs/cart/state/src/helpers/cart-items-entity-transform.spec.ts new file mode 100644 index 0000000000..ca5e8dd31f --- /dev/null +++ b/libs/cart/state/src/helpers/cart-items-entity-transform.spec.ts @@ -0,0 +1,66 @@ +import { TestBed } from '@angular/core/testing'; + +import { DaffCartItem } from '@daffodil/cart'; +import { DaffCartItemFactory } from '@daffodil/cart/testing'; +import { daffArrayToDict } from '@daffodil/core'; +import { + DaffOperationEntity, + DaffState, +} from '@daffodil/core/state'; + +import { daffCartItemsEntityTransform } from './cart-items-entity-transform'; + +describe('@daffodil/cart/state | daffCartItemsEntityTransform', () => { + let cartItemFactory: DaffCartItemFactory; + let cartItem: DaffCartItem; + let incomingCartItem: DaffCartItem; + let result: Array>; + + beforeEach(() => { + cartItemFactory = TestBed.inject(DaffCartItemFactory); + cartItem = cartItemFactory.create(); + }); + + describe('when the incoming cart item exists in the collection', () => { + beforeEach(() => { + incomingCartItem = cartItemFactory.create({ + id: cartItem.id, + qty: cartItem.qty + 1, + }); + result = daffCartItemsEntityTransform(daffArrayToDict([cartItem], (item) => item.id), [incomingCartItem]); + }); + + it('should set the cart item state to mutated', () => { + expect(result.find((item) => item.id === cartItem.id).daffState).toEqual(DaffState.Mutated); + }); + + it('should set operation entity values', () => { + result.forEach((item) => { + expect(item.daffErrors).toBeDefined(); + expect(item.daffState).toBeDefined(); + expect(item.daffTemp).toBeDefined(); + }); + }); + }); + + describe('when the incoming cart item does not exist in the collection', () => { + beforeEach(() => { + incomingCartItem = cartItemFactory.create({ + id: `new ${cartItem.id}`, + }); + result = daffCartItemsEntityTransform(daffArrayToDict([cartItem], (item) => item.id), [incomingCartItem]); + }); + + it('should set the incoming cart item state to added', () => { + expect(result.find((item) => item.id === incomingCartItem.id).daffState).toEqual(DaffState.Added); + }); + + it('should set operation entity values', () => { + result.forEach((item) => { + expect(item.daffErrors).toBeDefined(); + expect(item.daffState).toBeDefined(); + expect(item.daffTemp).toBeDefined(); + }); + }); + }); +}); diff --git a/libs/cart/state/src/helpers/cart-items-entity-transform.ts b/libs/cart/state/src/helpers/cart-items-entity-transform.ts new file mode 100644 index 0000000000..4762cabee5 --- /dev/null +++ b/libs/cart/state/src/helpers/cart-items-entity-transform.ts @@ -0,0 +1,31 @@ +import { Dictionary } from '@ngrx/entity'; + +import { DaffCartItem } from '@daffodil/cart'; +import { + DaffOperationEntity, + DaffState, +} from '@daffodil/core/state'; + +/** + * Transforms a list of cart items to item entities. + * Uses the list of previous cart items to infer the correct state of each entity. + * + * @param oldCartItems the dictionary of existing cart items. + * @param newCartItems the list of incoming cart items + * @returns a list of cart item operation entities + */ +export function daffCartItemsEntityTransform(oldCartItems: Dictionary, newCartItems: Array): Array> { + return newCartItems.map(newItem => { + const oldItem = oldCartItems[newItem.id]; + return { + ...newItem, + daffState: !oldItem + ? DaffState.Added + : oldItem?.qty !== newItem.qty + ? DaffState.Mutated + : DaffState.Stable, + daffErrors: [], + daffTemp: false, + }; + }); +} diff --git a/libs/cart/state/src/helpers/public_api.ts b/libs/cart/state/src/helpers/public_api.ts new file mode 100644 index 0000000000..985b8cd247 --- /dev/null +++ b/libs/cart/state/src/helpers/public_api.ts @@ -0,0 +1 @@ +export * from './cart-items-entity-transform'; diff --git a/libs/cart/state/src/models/public_api.ts b/libs/cart/state/src/models/public_api.ts index 78292d636e..9698033544 100644 --- a/libs/cart/state/src/models/public_api.ts +++ b/libs/cart/state/src/models/public_api.ts @@ -1,4 +1 @@ export * from './stateful-cart'; -export * from './stateful-cart-item'; -export * from './stateful-composite-cart-item'; -export * from './stateful-configurable-cart-item'; diff --git a/libs/cart/state/src/models/stateful-cart-item.ts b/libs/cart/state/src/models/stateful-cart-item.ts deleted file mode 100644 index 2ef4113e85..0000000000 --- a/libs/cart/state/src/models/stateful-cart-item.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { DaffCartItem } from '@daffodil/cart'; -import { DaffErrorable } from '@daffodil/core/state'; - -/** - * The state of the cart item is intended to enhance the client side UX like indicating when a cart - * item was recently added/updated. For states that indicate the completion of some process, the state is given - * a decay time based on the DaffCartItemStateDebounceTime injection token. For example when a cart item is - * added to the cart, the state of that item will be "New" for a designated time then will revert to the default state. - */ -export interface DaffStatefulCartItem extends DaffCartItem, DaffErrorable { - daffState: DaffCartItemStateEnum; -} - -export enum DaffCartItemStateEnum { - New = 'new', - Updated = 'updated', - Mutating = 'mutating', - Error = 'error', - Default = 'default' -} diff --git a/libs/cart/state/src/models/stateful-cart.ts b/libs/cart/state/src/models/stateful-cart.ts index b2d7f750ad..969f039a95 100644 --- a/libs/cart/state/src/models/stateful-cart.ts +++ b/libs/cart/state/src/models/stateful-cart.ts @@ -1,11 +1,15 @@ -import { DaffCart } from '@daffodil/cart'; - -import { DaffStatefulCartItem } from './stateful-cart-item'; +import { + DaffCart, + DaffCartItem, + DaffCompositeCartItem, + DaffConfigurableCartItem, +} from '@daffodil/cart'; +import { DaffOperationEntity } from '@daffodil/core/state'; /** * A cart with stateful cart items. */ export interface DaffStatefulCart extends DaffCart { - items: DaffStatefulCartItem[]; + items: Array>; } diff --git a/libs/cart/state/src/models/stateful-composite-cart-item.ts b/libs/cart/state/src/models/stateful-composite-cart-item.ts deleted file mode 100644 index 6080929c3d..0000000000 --- a/libs/cart/state/src/models/stateful-composite-cart-item.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { DaffCompositeCartItem } from '@daffodil/cart'; - -import { DaffStatefulCartItem } from './stateful-cart-item'; - -export interface DaffStatefulCompositeCartItem extends DaffCompositeCartItem, DaffStatefulCartItem {} diff --git a/libs/cart/state/src/models/stateful-configurable-cart-item.ts b/libs/cart/state/src/models/stateful-configurable-cart-item.ts deleted file mode 100644 index 09f9277759..0000000000 --- a/libs/cart/state/src/models/stateful-configurable-cart-item.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { DaffConfigurableCartItem } from '@daffodil/cart'; - -import { DaffStatefulCartItem } from './stateful-cart-item'; - -export interface DaffStatefulConfigurableCartItem extends DaffConfigurableCartItem, DaffStatefulCartItem {} diff --git a/libs/cart/state/src/public_api.ts b/libs/cart/state/src/public_api.ts index 9772920de5..c3699b3e82 100644 --- a/libs/cart/state/src/public_api.ts +++ b/libs/cart/state/src/public_api.ts @@ -2,8 +2,8 @@ export * from './actions/public_api'; export * from './cart-retrieval/public_api'; export * from './selectors/public_api'; export * from './reducers/public_api'; -export * from './models/public_api'; export * from './injection-tokens/public_api'; +export * from './models/public_api'; export { DaffCartStateModule } from './cart-state.module'; diff --git a/libs/cart/state/src/reducers/action-types.type.ts b/libs/cart/state/src/reducers/action-types.type.ts index 0a6775f338..18aed3216a 100644 --- a/libs/cart/state/src/reducers/action-types.type.ts +++ b/libs/cart/state/src/reducers/action-types.type.ts @@ -1,11 +1,7 @@ import { DaffCart, - DaffCartAddress, - DaffCartCoupon, DaffCartItemInput, DaffCartOrderResult, - DaffCartPaymentMethod, - DaffCartShippingRate, } from '@daffodil/cart'; import { @@ -21,25 +17,19 @@ import { DaffCartAddressActions, DaffCartOrderActions, } from '../actions/public_api'; -import { DaffStatefulCartItem } from '../models/public_api'; export type ActionTypes< T extends DaffCart = DaffCart, - V extends DaffStatefulCartItem = DaffStatefulCartItem, U extends DaffCartItemInput = DaffCartItemInput, - W extends DaffCartAddress = DaffCartAddress, - X extends DaffCartShippingRate = DaffCartShippingRate, - Y extends DaffCartPaymentMethod = DaffCartPaymentMethod, - Z extends DaffCartCoupon = DaffCartCoupon, TOrderResult extends DaffCartOrderResult = DaffCartOrderResult, > = DaffCartActions -| DaffCartItemActions -| DaffCartBillingAddressActions -| DaffCartShippingAddressActions -| DaffCartAddressActions -| DaffCartShippingMethodsActions -| DaffCartShippingInformationActions -| DaffCartPaymentActions -| DaffCartPaymentMethodsActions +| DaffCartItemActions +| DaffCartBillingAddressActions +| DaffCartShippingAddressActions +| DaffCartAddressActions +| DaffCartShippingMethodsActions +| DaffCartShippingInformationActions +| DaffCartPaymentActions +| DaffCartPaymentMethodsActions | DaffCartOrderActions -| DaffCartCouponActions; +| DaffCartCouponActions; diff --git a/libs/cart/state/src/reducers/cart-billing-address/cart-billing-address.reducer.ts b/libs/cart/state/src/reducers/cart-billing-address/cart-billing-address.reducer.ts index e397ce83a8..cef4567663 100644 --- a/libs/cart/state/src/reducers/cart-billing-address/cart-billing-address.reducer.ts +++ b/libs/cart/state/src/reducers/cart-billing-address/cart-billing-address.reducer.ts @@ -19,9 +19,9 @@ const addError = initializeErrorAdder(DaffCartOperationType.BillingAddress); const resetErrors = initializeErrorResetter(DaffCartOperationType.BillingAddress); const setLoading = initializeLoadingSetter(DaffCartOperationType.BillingAddress); -export function cartBillingAddressReducer( +export function cartBillingAddressReducer( state = daffCartReducerInitialState, - action: ActionTypes, + action: ActionTypes, ): DaffCartReducerState { switch (action.type) { case DaffCartBillingAddressActionTypes.CartBillingAddressLoadAction: diff --git a/libs/cart/state/src/reducers/cart-coupon/cart-coupon.reducer.ts b/libs/cart/state/src/reducers/cart-coupon/cart-coupon.reducer.ts index 4458f4b749..b196c5ec67 100644 --- a/libs/cart/state/src/reducers/cart-coupon/cart-coupon.reducer.ts +++ b/libs/cart/state/src/reducers/cart-coupon/cart-coupon.reducer.ts @@ -17,9 +17,9 @@ const addError = initializeErrorAdder(DaffCartOperationType.Coupon); const resetErrors = initializeErrorResetter(DaffCartOperationType.Coupon); const setLoading = initializeLoadingSetter(DaffCartOperationType.Coupon); -export function cartCouponReducer( +export function cartCouponReducer( state = daffCartReducerInitialState, - action: ActionTypes, + action: ActionTypes, ): DaffCartReducerState { switch (action.type) { case DaffCartCouponActionTypes.CartCouponListAction: diff --git a/libs/cart/state/src/reducers/cart-item-entities/adapter.ts b/libs/cart/state/src/reducers/cart-item-entities/adapter.ts new file mode 100644 index 0000000000..30b7dd6283 --- /dev/null +++ b/libs/cart/state/src/reducers/cart-item-entities/adapter.ts @@ -0,0 +1,27 @@ +import { createEntityAdapter } from '@ngrx/entity'; + +import { DaffCartItem } from '@daffodil/cart'; +import { + DaffOperationEntity, + DaffOperationEntityState, + DaffOperationEntityStateAdapter, +} from '@daffodil/core/state'; + +import { daffCartItemsEntityTransform } from '../../helpers/public_api'; + +export class DaffCartItemEntityStateAdapter extends DaffOperationEntityStateAdapter { + list = DaffOperationEntityState>(entities: T[], state: S): S { + return this.adapter.setAll( + daffCartItemsEntityTransform(state.entities, entities), + state, + ); + } +} + +/** + * Cart Item Entities Adapter for changing/overwriting entity state. + */ +export const daffCartItemEntitiesAdapter = (() => { + let cache; + return (): DaffOperationEntityStateAdapter => cache = cache ?? new DaffCartItemEntityStateAdapter(createEntityAdapter>({ selectId: item => item.id })); +})(); diff --git a/libs/cart/state/src/reducers/cart-item-entities/cart-item-entities-reducer-adapter.ts b/libs/cart/state/src/reducers/cart-item-entities/cart-item-entities-reducer-adapter.ts deleted file mode 100644 index 529e932b13..0000000000 --- a/libs/cart/state/src/reducers/cart-item-entities/cart-item-entities-reducer-adapter.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { - EntityAdapter, - createEntityAdapter, -} from '@ngrx/entity'; - -import { DaffStatefulCartItem } from '../../models/stateful-cart-item'; - -/** - * Cart Item Entities Adapter for changing/overwriting entity state. - */ -export const daffCartItemEntitiesAdapter = (() => { - let cache; - return (): EntityAdapter => - cache = cache || createEntityAdapter({ selectId: item => item.id }); -})(); diff --git a/libs/cart/state/src/reducers/cart-item-entities/cart-item-entities.reducer.spec.ts b/libs/cart/state/src/reducers/cart-item-entities/cart-item-entities.reducer.spec.ts index eae261d7ec..fb802b4564 100644 --- a/libs/cart/state/src/reducers/cart-item-entities/cart-item-entities.reducer.spec.ts +++ b/libs/cart/state/src/reducers/cart-item-entities/cart-item-entities.reducer.spec.ts @@ -1,36 +1,26 @@ import { TestBed } from '@angular/core/testing'; -import { EntityState } from '@ngrx/entity'; -import { DaffCart } from '@daffodil/cart'; +import { + DaffCart, + DaffCartItem, +} from '@daffodil/cart'; import { DaffCartItemListSuccess, DaffCartItemLoadSuccess, - DaffCartItemUpdateSuccess, - DaffCartItemAddSuccess, - DaffCartItemDeleteSuccess, - DaffCartLoadSuccess, - DaffCartClearSuccess, DaffCartItemDelete, - DaffCartItemStateEnum, DaffCartItemStateReset, DaffCartItemUpdate, - DaffResolveCartSuccess, - DaffStatefulCartItem, - DaffCartItemLoadFailure, - DaffCartItemDeleteFailure, - DaffCartItemUpdateFailure, DaffCartCreateSuccess, - DaffCartItemDeleteOutOfStockSuccess, - DaffCartLoadPartialSuccess, - DaffResolveCartPartialSuccess, daffCartItemEntitiesAdapter, } from '@daffodil/cart/state'; import { DaffStatefulCartItemFactory } from '@daffodil/cart/state/testing'; +import { DaffCartFactory } from '@daffodil/cart/testing'; import { - DaffCartFactory, - DaffCartItemFactory, -} from '@daffodil/cart/testing'; -import { DaffStateError } from '@daffodil/core/state'; + DaffOperationEntity, + DaffOperationEntityState, + DaffState, + DaffStateError, +} from '@daffodil/core/state'; import { daffCartItemEntitiesReducer } from './cart-item-entities.reducer'; @@ -61,51 +51,10 @@ describe('@daffodil/cart/state | daffCartItemEntitiesReducer', () => { }); }); - describe('when an existing cart item does not have a default daffState', () => { - let result: EntityState; - let stubStatefulCartItem: DaffStatefulCartItem; - let initialStateWithCartItem: EntityState; - - beforeEach(() => { - stubStatefulCartItem = new DaffStatefulCartItemFactory().create({ - id: 'id', - }); - initialStateWithCartItem = { - ...initialState, - entities: { - [stubStatefulCartItem.id]: { - ...stubStatefulCartItem, - daffState: DaffCartItemStateEnum.New, - }, - }, - }; - }); - - it('should retain the existing daffState when a CartItemListSuccessAction is triggered', () => { - const cartItemListSuccess = new DaffCartItemListSuccess([{ - ...stubStatefulCartItem, - daffState: DaffCartItemStateEnum.Default, - }]); - - result = daffCartItemEntitiesReducer(initialStateWithCartItem, cartItemListSuccess); - expect(result.entities[stubStatefulCartItem.id].daffState).toEqual(DaffCartItemStateEnum.New); - }); - - it('should retain the existing daffState when a CartItemLoadSuccessAction is triggered', () => { - const cartItemLoadSuccess = new DaffCartItemLoadSuccess({ - ...stubStatefulCartItem, - daffState: DaffCartItemStateEnum.Default, - }); - - result = daffCartItemEntitiesReducer(initialStateWithCartItem, cartItemLoadSuccess); - expect(result.entities[stubStatefulCartItem.id].daffState).toEqual(DaffCartItemStateEnum.New); - }); - }); - describe('when CartItemListSuccessAction is triggered', () => { - let cartItems: DaffStatefulCartItem[]; - let result: EntityState; + let cartItems: DaffOperationEntity[]; + let result: DaffOperationEntityState>; beforeEach(() => { cartItems = statefulCartItemFactory.createMany(2); @@ -119,7 +68,7 @@ describe('@daffodil/cart/state | daffCartItemEntitiesReducer', () => { }); it('sets expected statefulCartItem on state', () => { - expect(result.entities[cartItems[0].id]).toEqual(cartItems[0]); + expect(result.entities[cartItems[0].id].id).toEqual(cartItems[0].id); }); it('should reset the cart item\'s errors', () => { @@ -129,8 +78,8 @@ describe('@daffodil/cart/state | daffCartItemEntitiesReducer', () => { describe('when CartItemLoadSuccessAction is triggered', () => { - let statefulCartItem: DaffStatefulCartItem; - let result: EntityState; + let statefulCartItem: DaffOperationEntity; + let result: DaffOperationEntityState>; beforeEach(() => { statefulCartItem = statefulCartItemFactory.create(); @@ -150,39 +99,39 @@ describe('@daffodil/cart/state | daffCartItemEntitiesReducer', () => { describe('when CartItemStateResetAction is triggered', () => { - let stubCartItem: DaffStatefulCartItem; - let result: EntityState; + let stubCartItem: DaffOperationEntity; + let result: DaffOperationEntityState>; let testInitialState; beforeEach(() => { - stubCartItem = new DaffStatefulCartItemFactory().create(); + stubCartItem = statefulCartItemFactory.create(); testInitialState = { ids: [stubCartItem.id.toString()], entities: { [stubCartItem.id]: { ...stubCartItem, - daffState: DaffCartItemStateEnum.New, + daffState: DaffState.Added, }, }, }; - const cartItemStateReset = new DaffCartItemStateReset(); + const cartItemStateReset = new DaffCartItemStateReset(stubCartItem.id); result = daffCartItemEntitiesReducer(testInitialState, cartItemStateReset); }); it('resets the state of all the cart items to default', () => { - expect(result.entities[stubCartItem.id].daffState).toEqual(DaffCartItemStateEnum.Default); + expect(result.entities[stubCartItem.id].daffState).toEqual(DaffState.Stable); }); }); describe('when CartItemUpdateAction is triggered', () => { - let stubStatefulCartItem: DaffStatefulCartItem; - let result: EntityState; + let stubStatefulCartItem: DaffOperationEntity; + let result: DaffOperationEntityState>; let testInitialState; beforeEach(() => { - stubStatefulCartItem = new DaffStatefulCartItemFactory().create(); + stubStatefulCartItem = statefulCartItemFactory.create(); testInitialState = { ids: [stubStatefulCartItem.id.toString()], entities: { @@ -195,18 +144,18 @@ describe('@daffodil/cart/state | daffCartItemEntitiesReducer', () => { }); it('sets the updating cart item state to mutating', () => { - expect(result.entities[stubStatefulCartItem.id].daffState).toEqual(DaffCartItemStateEnum.Mutating); + expect(result.entities[stubStatefulCartItem.id].daffState).toEqual(DaffState.Mutating); }); }); describe('when CartItemDeleteAction is triggered', () => { - let stubCartItem: DaffStatefulCartItem; - let result: EntityState; + let stubCartItem: DaffOperationEntity; + let result: DaffOperationEntityState>; let testInitialState; beforeEach(() => { - stubCartItem = new DaffStatefulCartItemFactory().create(); + stubCartItem = statefulCartItemFactory.create(); testInitialState = { ids: [stubCartItem.id.toString()], entities: { @@ -218,80 +167,13 @@ describe('@daffodil/cart/state | daffCartItemEntitiesReducer', () => { result = daffCartItemEntitiesReducer(testInitialState, cartItemDeleteAction); }); - it('sets the cart item state to mutating', () => { - expect(result.entities[stubCartItem.id].daffState).toEqual(DaffCartItemStateEnum.Mutating); - }); - }); - - describe('when CartItemLoadFailureAction is triggered', () => { - let statefulCartItem: DaffStatefulCartItem; - let result: EntityState; - - beforeEach(() => { - error = { - code: 'code', - message: 'message', - }; - statefulCartItem = statefulCartItemFactory.create(); - const cartItemLoadSuccess = new DaffCartItemLoadSuccess(statefulCartItem); - const cartItemLoadFailure = new DaffCartItemLoadFailure([error], statefulCartItem.id); - - result = daffCartItemEntitiesReducer(daffCartItemEntitiesReducer(initialState, cartItemLoadSuccess), cartItemLoadFailure); - }); - - it('should reset daffState on the cart item', () => { - expect(result.entities[statefulCartItem.id].daffState).toEqual(DaffCartItemStateEnum.Error); - }); - }); - - describe('when CartItemDeleteFailureAction is triggered', () => { - let statefulCartItem: DaffStatefulCartItem; - let result: EntityState; - - beforeEach(() => { - error = { - code: 'code', - message: 'message', - }; - statefulCartItem = statefulCartItemFactory.create(); - const cartItemLoadSuccess = new DaffCartItemLoadSuccess(statefulCartItem); - const cartItemDeleteFailure = new DaffCartItemDeleteFailure([error], statefulCartItem.id); - - result = daffCartItemEntitiesReducer(daffCartItemEntitiesReducer(initialState, cartItemLoadSuccess), cartItemDeleteFailure); - }); - - it('should reset daffState on the cart item', () => { - expect(result.entities[statefulCartItem.id].daffState).toEqual(DaffCartItemStateEnum.Error); - }); - }); - - describe('when CartItemUpdateFailureAction is triggered', () => { - let statefulCartItem: DaffStatefulCartItem; - let result: EntityState; - - beforeEach(() => { - error = { - code: 'code', - message: 'message', - }; - statefulCartItem = statefulCartItemFactory.create(); - const cartItemLoadSuccess = new DaffCartItemLoadSuccess(statefulCartItem); - const cartItemUpdateFailure = new DaffCartItemUpdateFailure([error], statefulCartItem.id); - - result = daffCartItemEntitiesReducer(daffCartItemEntitiesReducer(initialState, cartItemLoadSuccess), cartItemUpdateFailure); - }); - - it('should reset daffState on the cart item', () => { - expect(result.entities[statefulCartItem.id].daffState).toEqual(DaffCartItemStateEnum.Error); - }); - - it('should add the error to the cart item\'s errors', () => { - expect(result.entities[statefulCartItem.id].daffErrors).toContain(error); + it('sets the cart item state to deleting', () => { + expect(result.entities[stubCartItem.id].daffState).toEqual(DaffState.Deleting); }); }); describe('when CartCreateSuccessAction is triggered', () => { - let result: EntityState; + let result: DaffOperationEntityState>; beforeEach(() => { const cartCreateSuccess = new DaffCartCreateSuccess({}); diff --git a/libs/cart/state/src/reducers/cart-item-entities/cart-item-entities.reducer.ts b/libs/cart/state/src/reducers/cart-item-entities/cart-item-entities.reducer.ts index e7333bee9c..6df615eb77 100644 --- a/libs/cart/state/src/reducers/cart-item-entities/cart-item-entities.reducer.ts +++ b/libs/cart/state/src/reducers/cart-item-entities/cart-item-entities.reducer.ts @@ -1,25 +1,17 @@ -import { - Dictionary, - EntityState, -} from '@ngrx/entity'; - import { DaffCartItemInput, DaffCart, + daffCartItemInputToItemTransform, } from '@daffodil/cart'; +import { DaffOperationEntityState } from '@daffodil/core/state'; -import { daffCartItemEntitiesAdapter } from './cart-item-entities-reducer-adapter'; +import { daffCartItemEntitiesAdapter } from './adapter'; import { DaffCartItemActionTypes, DaffCartActionTypes, DaffCartActions, DaffCartItemActions, - DaffCartOrderActionTypes, } from '../../actions/public_api'; -import { - DaffCartItemStateEnum, - DaffStatefulCartItem, -} from '../../models/stateful-cart-item'; /** * Reducer function that catches actions and changes/overwrites product entities state. @@ -29,60 +21,59 @@ import { * @returns CartItem entities state */ export function daffCartItemEntitiesReducer< - T extends DaffStatefulCartItem = DaffStatefulCartItem, + T extends DaffCart = DaffCart, U extends DaffCartItemInput = DaffCartItemInput, - V extends DaffCart = DaffCart, >( - state = daffCartItemEntitiesAdapter().getInitialState(), - action: DaffCartItemActions | DaffCartActions): EntityState { - const adapter = daffCartItemEntitiesAdapter(); + state = daffCartItemEntitiesAdapter().getInitialState(), + action: DaffCartItemActions | DaffCartActions, +): DaffOperationEntityState { + const adapter = daffCartItemEntitiesAdapter(); switch (action.type) { case DaffCartItemActionTypes.CartItemListSuccessAction: - return adapter.setAll(action.payload.map(item => ({ - ...item, - daffState: getDaffState(state.entities[item.id]) || DaffCartItemStateEnum.Default, - daffErrors: [], - })), state); + return adapter.list(action.payload, state); case DaffCartItemActionTypes.CartItemLoadSuccessAction: - return adapter.upsertOne({ - ...action.payload, - daffState: getDaffState(state.entities[action.payload.id]) || DaffCartItemStateEnum.Default, - daffErrors: [], - }, state); + return adapter.load(action.payload, state); case DaffCartItemActionTypes.CartItemDeleteFailureAction: case DaffCartItemActionTypes.CartItemLoadFailureAction: case DaffCartItemActionTypes.CartItemUpdateFailureAction: - return adapter.upsertOne({ - ...state.entities[action.itemId], - daffState: DaffCartItemStateEnum.Error, - daffErrors: state.entities[action.itemId]?.daffErrors?.concat(action.payload) || action.payload, - }, state); + return adapter.operationFailed( + action.itemId, + action.payload, + state, + ); + + case DaffCartItemActionTypes.CartItemAddFailureAction: + return adapter.operationFailed( + action.placeholderId, + action.payload, + state, + ); case DaffCartItemActionTypes.CartItemStateResetAction: - return adapter.setAll(Object.keys(state.entities).map(key => ({ - ...state.entities[key], - daffState: DaffCartItemStateEnum.Default, - })), state); + return adapter.resetState(action.itemId, state); case DaffCartItemActionTypes.CartItemUpdateAction: - case DaffCartItemActionTypes.CartItemDeleteAction: - return adapter.upsertOne({ - ...state.entities[action.itemId], - daffState: DaffCartItemStateEnum.Mutating, + return adapter.preupdate({ + ...action.changes, + id: action.itemId, }, state); + case DaffCartItemActionTypes.CartItemDeleteAction: + return adapter.preremove(action.itemId, state); + + case DaffCartItemActionTypes.CartItemAddAction: + return adapter.preadd( + daffCartItemInputToItemTransform(action.input), + state, + action.placeholderId, + ); + case DaffCartActionTypes.CartCreateSuccessAction: - return adapter.removeAll(state); + return adapter.list([], state); default: return state; } } - -function getDaffState(item: T): DaffCartItemStateEnum { - return item?.daffState; -} - - diff --git a/libs/cart/state/src/reducers/cart-item-entities/retrieval-actions.reducer.spec.ts b/libs/cart/state/src/reducers/cart-item-entities/retrieval-actions.reducer.spec.ts index 470a36b51d..54778462f6 100644 --- a/libs/cart/state/src/reducers/cart-item-entities/retrieval-actions.reducer.spec.ts +++ b/libs/cart/state/src/reducers/cart-item-entities/retrieval-actions.reducer.spec.ts @@ -1,21 +1,23 @@ import { TestBed } from '@angular/core/testing'; -import { EntityState } from '@ngrx/entity'; import { Action, ActionReducer, } from '@ngrx/store'; +import { DaffCartItem } from '@daffodil/cart'; import { - DaffCartItemStateEnum, DaffCartRetrievalAction, - DaffStatefulCart, - DaffStatefulCartItem, daffCartItemEntitiesAdapter, + DaffStatefulCart, } from '@daffodil/cart/state'; import { DaffStatefulCartItemFactory } from '@daffodil/cart/state/testing'; import { DaffCartFactory } from '@daffodil/cart/testing'; -import { DaffStateError } from '@daffodil/core/state'; +import { + DaffOperationEntity, + DaffOperationEntityState, + DaffStateError, +} from '@daffodil/core/state'; import { daffCartItemEntitiesRetrievalActionsReducerFactory } from './retrieval-actions.reducer'; @@ -35,9 +37,9 @@ describe('@daffodil/cart/state | daffCartItemEntitiesRetrievalActionsReducerFact let error: DaffStateError; let cart: DaffStatefulCart; - let reducer: ActionReducer>; - let result: EntityState; - let initialState: EntityState; + let reducer: ActionReducer>>; + let result: DaffOperationEntityState>; + let initialState: DaffOperationEntityState>; beforeEach(() => { initialState = daffCartItemEntitiesAdapter().getInitialState(); @@ -76,57 +78,7 @@ describe('@daffodil/cart/state | daffCartItemEntitiesRetrievalActionsReducerFact it('should store the cart items', () => { cart.items.forEach((item) => { - expect(result.entities[item.id]).toEqual(item); - }); - }); - - describe('and when an incoming cart item has an existing daffState', () => { - beforeEach(() => { - cart.items[0].daffState = DaffCartItemStateEnum.New; - result = reducer(initialState, new DirectAction(cart)); - }); - - it('should retain the existing daffState', () => { - expect(result.entities[cart.items[0].id].daffState).toEqual(cart.items[0].daffState); - }); - }); - - describe('and when an incoming cart item does not have an existing daffState', () => { - beforeEach(() => { - cart.items[0].daffState = undefined; - result = reducer(initialState, new DirectAction(cart)); - }); - - describe('and the item in state has an existing daffState', () => { - let state: EntityState; - - beforeEach(() => { - state = daffCartItemEntitiesAdapter().addOne({ - ...cart.items[0], - daffState: DaffCartItemStateEnum.New, - }, initialState); - result = reducer(state, new DirectAction(cart)); - }); - - it('should retain the existing daffState in entity state', () => { - expect(result.entities[cart.items[0].id].daffState).toEqual(state.entities[cart.items[0].id].daffState); - }); - }); - - describe('and the item in state does not have an existing daffState', () => { - let state: EntityState; - - beforeEach(() => { - state = daffCartItemEntitiesAdapter().addOne({ - ...cart.items[0], - daffState: undefined, - }, initialState); - result = reducer(state, new DirectAction(cart)); - }); - - it('should initialize the daffState field', () => { - expect(result.entities[cart.items[0].id].daffState).toEqual(DaffCartItemStateEnum.Default); - }); + expect(result.entities[item.id].id).toEqual(item.id); }); }); }); @@ -138,57 +90,7 @@ describe('@daffodil/cart/state | daffCartItemEntitiesRetrievalActionsReducerFact it('should store the cart items', () => { cart.items.forEach((item) => { - expect(result.entities[item.id]).toEqual(item); - }); - }); - - describe('when an incoming cart item has an existing daffState', () => { - beforeEach(() => { - cart.items[0].daffState = DaffCartItemStateEnum.New; - result = reducer(initialState, new TransformAction(cart)); - }); - - it('should retain the existing daffState', () => { - expect(result.entities[cart.items[0].id].daffState).toEqual(cart.items[0].daffState); - }); - }); - - describe('when an incoming cart item does not have an existing daffState', () => { - beforeEach(() => { - cart.items[0].daffState = undefined; - result = reducer(initialState, new TransformAction(cart)); - }); - - describe('and the item in state has an existing daffState', () => { - let state: EntityState; - - beforeEach(() => { - state = daffCartItemEntitiesAdapter().addOne({ - ...cart.items[0], - daffState: DaffCartItemStateEnum.New, - }, initialState); - result = reducer(state, new TransformAction(cart)); - }); - - it('should retain the existing daffState in entity state', () => { - expect(result.entities[cart.items[0].id].daffState).toEqual(state.entities[cart.items[0].id].daffState); - }); - }); - - describe('and the item in state does not have an existing daffState', () => { - let state: EntityState; - - beforeEach(() => { - state = daffCartItemEntitiesAdapter().addOne({ - ...cart.items[0], - daffState: undefined, - }, initialState); - result = reducer(state, new TransformAction(cart)); - }); - - it('should initialize the daffState field', () => { - expect(result.entities[cart.items[0].id].daffState).toEqual(DaffCartItemStateEnum.Default); - }); + expect(result.entities[item.id].id).toEqual(item.id); }); }); }); diff --git a/libs/cart/state/src/reducers/cart-item-entities/retrieval-actions.reducer.ts b/libs/cart/state/src/reducers/cart-item-entities/retrieval-actions.reducer.ts index 437888984c..50cca5cc77 100644 --- a/libs/cart/state/src/reducers/cart-item-entities/retrieval-actions.reducer.ts +++ b/libs/cart/state/src/reducers/cart-item-entities/retrieval-actions.reducer.ts @@ -1,38 +1,28 @@ -import { EntityState } from '@ngrx/entity'; import { Action } from '@ngrx/store'; import { DaffCart } from '@daffodil/cart'; +import { DaffOperationEntityState } from '@daffodil/core/state'; -import { daffCartItemEntitiesAdapter } from './cart-item-entities-reducer-adapter'; +import { daffCartItemEntitiesAdapter } from './adapter'; import { DaffCartRetrievalActionInjection, daffCartRetrievalGetResponse, } from '../../cart-retrieval/public_api'; -import { - DaffCartItemStateEnum, - DaffStatefulCartItem, -} from '../../models/stateful-cart-item'; /** * Reducer function factory that updates cart item entities according to a list of passed cart retrieval actions. * See {@link DaffCartRetrievalActionInjection}. */ -export function daffCartItemEntitiesRetrievalActionsReducerFactory< - T extends DaffStatefulCartItem = DaffStatefulCartItem, - V extends DaffCart = DaffCart, ->(retrievalActions: DaffCartRetrievalActionInjection[]) { +export function daffCartItemEntitiesRetrievalActionsReducerFactory(retrievalActions: DaffCartRetrievalActionInjection[]) { return ( - state = daffCartItemEntitiesAdapter().getInitialState(), + state = daffCartItemEntitiesAdapter().getInitialState(), action: Action, - ): EntityState => { - const adapter = daffCartItemEntitiesAdapter(); - const cart = daffCartRetrievalGetResponse(action, retrievalActions); + ): DaffOperationEntityState => { + const adapter = daffCartItemEntitiesAdapter(); + const cart = daffCartRetrievalGetResponse(action, retrievalActions); return cart?.items - ? adapter.setAll(cart.items.map((item: T) => ({ - ...item, - daffState: item.daffState || state.entities[item.id]?.daffState || DaffCartItemStateEnum.Default, - })), state) + ? adapter.list(cart.items, state) : state; }; } diff --git a/libs/cart/state/src/reducers/cart-item-entities/set-state.meta-reducer.ts b/libs/cart/state/src/reducers/cart-item-entities/set-state.meta-reducer.ts deleted file mode 100644 index 058e1d6b7f..0000000000 --- a/libs/cart/state/src/reducers/cart-item-entities/set-state.meta-reducer.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Dictionary } from '@ngrx/entity'; -import { ActionReducer } from '@ngrx/store'; - -import { - DaffCart, - DaffCartOrderResult, -} from '@daffodil/cart'; - -import { - DaffCartItemActionTypes, - DaffCartItemActions, -} from '../../actions/public_api'; -import { - DaffCartItemStateEnum, - DaffStatefulCartItem, -} from '../../models/public_api'; -import { DaffCartReducersState } from '../cart-reducers-state.interface'; - -type Reducer = ActionReducer, DaffCartItemActions>; - -/** - * A meta reducer for determining and setting the correct `daffState` values on cart items. - */ -export function daffCartSetItemStateMetaReducer(reducer: Reducer): Reducer { - return (state, action) => { - switch (action.type) { - case DaffCartItemActionTypes.CartItemAddSuccessAction: - return reducer( - state, - { - ...action, - payload: { - ...action.payload, - items: updateAddedCartItemState(state.cartItems.entities, (action.payload.items).map((item) => ({ - ...item, - daffErrors: [], - }))), - }, - }, - ); - - case DaffCartItemActionTypes.CartItemUpdateSuccessAction: - return reducer( - state, - { - ...action, - payload: { - ...action.payload, - items: updateMutatedCartItemState((action.payload.items).map((item) => item.id === action.itemId - ? { - ...item, - daffErrors: [], - } - : item), state.cartItems.entities, action.itemId), - }, - }, - ); - - default: - return reducer(state, action); - } - }; -} - -function updateAddedCartItemState(oldCartItems: Dictionary, newCartItems: T[]): T[] { - return newCartItems.map(newItem => { - const oldItem = oldCartItems[newItem.id]; - switch (true) { - case !oldItem: - return { ...newItem, daffState: DaffCartItemStateEnum.New, daffErrors: []}; - case oldItem?.qty !== newItem.qty: - return { ...newItem, daffState: DaffCartItemStateEnum.Updated, daffErrors: []}; - default: - return newItem; - } - }); -} - -function updateMutatedCartItemState(responseItems: T[], stateItems: Dictionary, itemId: T['id']): T[] { - return responseItems.map(item => item.id === itemId ? - { ...item, daffState: DaffCartItemStateEnum.Updated, daffErrors: []} : - { ...item, daffState: stateItems[item.id]?.daffState || DaffCartItemStateEnum.Default }); -} diff --git a/libs/cart/state/src/reducers/cart-item/cart-item.reducer.spec.ts b/libs/cart/state/src/reducers/cart-item/cart-item.reducer.spec.ts index 374890fdb2..79267e2077 100644 --- a/libs/cart/state/src/reducers/cart-item/cart-item.reducer.spec.ts +++ b/libs/cart/state/src/reducers/cart-item/cart-item.reducer.spec.ts @@ -2,6 +2,7 @@ import { TestBed } from '@angular/core/testing'; import { DaffCart, + DaffCartItem, DaffCartItemInputType, } from '@daffodil/cart'; import { DaffCartDriverErrorCodes } from '@daffodil/cart/driver'; @@ -22,7 +23,6 @@ import { DaffCartItemListSuccess, DaffCartItemListFailure, daffCartReducerInitialState, - DaffStatefulCartItem, DaffCartItemUpdate, DaffCartItemDelete, DaffCartItemDeleteOutOfStockSuccess, @@ -34,6 +34,7 @@ import { DaffCartFactory } from '@daffodil/cart/testing'; import { DaffStateError, DaffState, + DaffOperationEntity, } from '@daffodil/core/state'; import { cartItemReducer } from './cart-item.reducer'; @@ -43,7 +44,7 @@ describe('@daffodil/cart/state | cartItemReducer', () => { let statefulCartItemFactory: DaffStatefulCartItemFactory; let cart: DaffCart; - let cartItem: DaffStatefulCartItem; + let cartItem: DaffOperationEntity; let result: DaffCartReducerState; @@ -314,7 +315,7 @@ describe('@daffodil/cart/state | cartItemReducer', () => { let state: DaffCartReducerState; beforeEach(() => { - const cartItemAddActionSuccess = new DaffCartItemAddSuccess(cart); + const cartItemAddActionSuccess = new DaffCartItemAddSuccess(cart, cartItem.id); state = { ...daffCartReducerInitialState, loading: { @@ -386,7 +387,7 @@ describe('@daffodil/cart/state | cartItemReducer', () => { describe('when CartItemLoadSuccessAction is triggered', () => { let state: DaffCartReducerState; - let newCartItem: DaffStatefulCartItem; + let newCartItem: DaffOperationEntity; beforeEach(() => { newCartItem = { diff --git a/libs/cart/state/src/reducers/cart-item/cart-item.reducer.ts b/libs/cart/state/src/reducers/cart-item/cart-item.reducer.ts index 43250e0ea0..d93965ef50 100644 --- a/libs/cart/state/src/reducers/cart-item/cart-item.reducer.ts +++ b/libs/cart/state/src/reducers/cart-item/cart-item.reducer.ts @@ -17,9 +17,9 @@ const addError = initializeErrorAdder(DaffCartOperationType.Item); const resetErrors = initializeErrorResetter(DaffCartOperationType.Item); const setLoading = initializeLoadingSetter(DaffCartOperationType.Item); -export function cartItemReducer( +export function cartItemReducer( state = daffCartReducerInitialState, - action: ActionTypes, + action: ActionTypes, ): DaffCartReducerState { switch (action.type) { case DaffCartItemActionTypes.CartItemListAction: diff --git a/libs/cart/state/src/reducers/cart-order/cart-order.reducer.ts b/libs/cart/state/src/reducers/cart-order/cart-order.reducer.ts index 6b44890b5d..3be95f82d8 100644 --- a/libs/cart/state/src/reducers/cart-order/cart-order.reducer.ts +++ b/libs/cart/state/src/reducers/cart-order/cart-order.reducer.ts @@ -12,7 +12,6 @@ import { DaffLoadingState } from '@daffodil/core/state'; import { daffCartOrderInitialState } from './cart-order-initial-state'; import { DaffCartOrderReducerState } from './cart-order-state.interface'; -import { DaffStatefulCartItem } from '../..'; import { DaffCartActionTypes, DaffCartAddressActionTypes, @@ -30,12 +29,7 @@ export function daffCartOrderReducer, ): DaffCartOrderReducerState { diff --git a/libs/cart/state/src/reducers/cart-payment-methods/cart-payment-methods.reducer.ts b/libs/cart/state/src/reducers/cart-payment-methods/cart-payment-methods.reducer.ts index 866bad1674..91c2328e71 100644 --- a/libs/cart/state/src/reducers/cart-payment-methods/cart-payment-methods.reducer.ts +++ b/libs/cart/state/src/reducers/cart-payment-methods/cart-payment-methods.reducer.ts @@ -16,9 +16,9 @@ const addError = initializeErrorAdder(DaffCartOperationType.PaymentMethods); const resetErrors = initializeErrorResetter(DaffCartOperationType.PaymentMethods); const setLoading = initializeLoadingSetter(DaffCartOperationType.PaymentMethods); -export function cartPaymentMethodsReducer( +export function cartPaymentMethodsReducer( state = daffCartReducerInitialState, - action: ActionTypes, + action: ActionTypes, ): DaffCartReducerState { switch (action.type) { case DaffCartPaymentMethodsActionTypes.CartPaymentMethodsLoadAction: diff --git a/libs/cart/state/src/reducers/cart-payment/cart-payment.reducer.ts b/libs/cart/state/src/reducers/cart-payment/cart-payment.reducer.ts index 74ff7d7e13..d1057d57e4 100644 --- a/libs/cart/state/src/reducers/cart-payment/cart-payment.reducer.ts +++ b/libs/cart/state/src/reducers/cart-payment/cart-payment.reducer.ts @@ -17,9 +17,9 @@ const addError = initializeErrorAdder(DaffCartOperationType.Payment); const resetErrors = initializeErrorResetter(DaffCartOperationType.Payment); const setLoading = initializeLoadingSetter(DaffCartOperationType.Payment); -export function cartPaymentReducer( +export function cartPaymentReducer( state = daffCartReducerInitialState, - action: ActionTypes, + action: ActionTypes, ): DaffCartReducerState { switch (action.type) { case DaffCartPaymentActionTypes.CartPaymentLoadAction: diff --git a/libs/cart/state/src/reducers/cart-reducers-state.interface.ts b/libs/cart/state/src/reducers/cart-reducers-state.interface.ts index 1710699d7e..20db639a17 100644 --- a/libs/cart/state/src/reducers/cart-reducers-state.interface.ts +++ b/libs/cart/state/src/reducers/cart-reducers-state.interface.ts @@ -1,29 +1,25 @@ -import { EntityState } from '@ngrx/entity'; - import { DaffCart, DaffCartOrderResult, } from '@daffodil/cart'; +import { DaffOperationEntityState } from '@daffodil/core/state'; import { DaffCartOrderReducerState } from './cart-order/cart-order-state.interface'; import { DaffCartReducerState } from './cart-state.interface'; import { DAFF_CART_STORE_FEATURE_KEY } from './cart-store-feature-key'; -import { DaffStatefulCartItem } from '../models/stateful-cart-item'; export interface DaffCartReducersState< T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem > { cart: DaffCartReducerState; - cartItems: EntityState; + cartItems: DaffOperationEntityState; order: DaffCartOrderReducerState; } export interface DaffCartStateRootSlice< T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem > { - [DAFF_CART_STORE_FEATURE_KEY]: DaffCartReducersState; + [DAFF_CART_STORE_FEATURE_KEY]: DaffCartReducersState; } diff --git a/libs/cart/state/src/reducers/cart-resolve/cart-resolve.reducer.ts b/libs/cart/state/src/reducers/cart-resolve/cart-resolve.reducer.ts index a02d416df8..5587746c9a 100644 --- a/libs/cart/state/src/reducers/cart-resolve/cart-resolve.reducer.ts +++ b/libs/cart/state/src/reducers/cart-resolve/cart-resolve.reducer.ts @@ -8,7 +8,7 @@ import { DaffCartReducerState } from '../cart-state.interface'; export function cartResolveReducer( state = daffCartReducerInitialState, - action: ActionTypes, + action: ActionTypes, ): DaffCartReducerState { switch (action.type) { case DaffCartActionTypes.ResolveCartAction: diff --git a/libs/cart/state/src/reducers/cart-shipping-methods/cart-shipping-methods.reducer.spec.ts b/libs/cart/state/src/reducers/cart-shipping-methods/cart-shipping-methods.reducer.spec.ts index 106d59e6e0..6d7a1d8262 100644 --- a/libs/cart/state/src/reducers/cart-shipping-methods/cart-shipping-methods.reducer.spec.ts +++ b/libs/cart/state/src/reducers/cart-shipping-methods/cart-shipping-methods.reducer.spec.ts @@ -31,7 +31,7 @@ describe('@daffodil/cart/state | cartShippingMethodsReducer', () => { beforeEach(() => { cartFactory = TestBed.inject(DaffCartFactory); - cartShippingRateFactory = TestBed.inject(DaffCartShippingRateFactory); + cartShippingRateFactory = TestBed.inject(DaffCartShippingRateFactory); cart = cartFactory.create(); mockCartShippingRate = cartShippingRateFactory.create(); diff --git a/libs/cart/state/src/reducers/cart-shipping-methods/cart-shipping-methods.reducer.ts b/libs/cart/state/src/reducers/cart-shipping-methods/cart-shipping-methods.reducer.ts index 88af0ee5c3..7357a18828 100644 --- a/libs/cart/state/src/reducers/cart-shipping-methods/cart-shipping-methods.reducer.ts +++ b/libs/cart/state/src/reducers/cart-shipping-methods/cart-shipping-methods.reducer.ts @@ -16,9 +16,9 @@ const addError = initializeErrorAdder(DaffCartOperationType.ShippingMethods); const resetErrors = initializeErrorResetter(DaffCartOperationType.ShippingMethods); const setLoading = initializeLoadingSetter(DaffCartOperationType.ShippingMethods); -export function cartShippingMethodsReducer( +export function cartShippingMethodsReducer( state = daffCartReducerInitialState, - action: ActionTypes, + action: ActionTypes, ): DaffCartReducerState { switch (action.type) { case DaffCartShippingMethodsActionTypes.CartShippingMethodsLoadAction: diff --git a/libs/cart/state/src/reducers/cart/cart.reducer.ts b/libs/cart/state/src/reducers/cart/cart.reducer.ts index f0e59211a1..ec037a15b7 100644 --- a/libs/cart/state/src/reducers/cart/cart.reducer.ts +++ b/libs/cart/state/src/reducers/cart/cart.reducer.ts @@ -16,9 +16,9 @@ const addError = initializeErrorAdder(DaffCartOperationType.Cart); const resetErrors = initializeErrorResetter(DaffCartOperationType.Cart); const setLoading = initializeLoadingSetter(DaffCartOperationType.Cart); -export function cartReducer( +export function cartReducer( state = daffCartReducerInitialState, - action: ActionTypes, + action: ActionTypes, ): DaffCartReducerState { switch (action.type) { case DaffCartActionTypes.ResolveCartAction: diff --git a/libs/cart/state/src/reducers/public_api.ts b/libs/cart/state/src/reducers/public_api.ts index 8342951446..92ef534cb5 100644 --- a/libs/cart/state/src/reducers/public_api.ts +++ b/libs/cart/state/src/reducers/public_api.ts @@ -18,7 +18,7 @@ export { DaffCartOrderReducerState } from './cart-order/cart-order-state.interfa export { daffCartOrderInitialState } from './cart-order/cart-order-initial-state'; export { DAFF_CART_STORE_FEATURE_KEY } from './cart-store-feature-key'; -export { daffCartItemEntitiesAdapter } from './cart-item-entities/cart-item-entities-reducer-adapter'; +export { daffCartItemEntitiesAdapter } from './cart-item-entities/adapter'; export { daffCartItemEntitiesRetrievalActionsReducerFactory } from './cart-item-entities/retrieval-actions.reducer'; export { daffCartRetrievalActionsReducerFactory } from './cart/retrieval-actions.reducer'; diff --git a/libs/cart/state/src/reducers/token/config.token.ts b/libs/cart/state/src/reducers/token/config.token.ts index 171df04ade..81ab972ce9 100644 --- a/libs/cart/state/src/reducers/token/config.token.ts +++ b/libs/cart/state/src/reducers/token/config.token.ts @@ -8,7 +8,6 @@ import { StoreConfig } from '@ngrx/store'; import { DaffCart } from '@daffodil/cart'; import { DAFF_CART_META_REDUCERS } from './meta.token'; -import { daffCartSetItemStateMetaReducer } from '../cart-item-entities/set-state.meta-reducer'; import { DaffCartReducersState } from '../cart-reducers-state.interface'; /** @@ -21,10 +20,7 @@ export const DAFF_CART_STORE_CONFIG = new InjectionToken ({ - metaReducers: [ - daffCartSetItemStateMetaReducer, - ...inject(DAFF_CART_META_REDUCERS), - ], + metaReducers: inject(DAFF_CART_META_REDUCERS), }), }, ); diff --git a/libs/cart/state/src/reducers/token/extra.token.ts b/libs/cart/state/src/reducers/token/extra.token.ts index 49ba9bb087..ca816a570b 100644 --- a/libs/cart/state/src/reducers/token/extra.token.ts +++ b/libs/cart/state/src/reducers/token/extra.token.ts @@ -9,7 +9,6 @@ import { DaffCartOrderResult, } from '@daffodil/cart'; -import { DaffStatefulCartItem } from '../../models/public_api'; import { DaffCartReducersState } from '../cart-reducers-state.interface'; /** @@ -37,9 +36,8 @@ export const DAFF_CART_EXTRA_REDUCERS = new InjectionToken( - ...reducers: ActionReducer>[] + ...reducers: ActionReducer>[] ): Provider[] { return reducers.map(reducer => ({ provide: DAFF_CART_EXTRA_REDUCERS, diff --git a/libs/cart/state/src/selectors/cart-feature.selector.ts b/libs/cart/state/src/selectors/cart-feature.selector.ts index 3f2f28ac13..479eeb79e0 100644 --- a/libs/cart/state/src/selectors/cart-feature.selector.ts +++ b/libs/cart/state/src/selectors/cart-feature.selector.ts @@ -8,7 +8,6 @@ import { DaffCartOrderResult, } from '@daffodil/cart'; -import { DaffStatefulCartItem } from '../models/stateful-cart-item'; import { DaffCartStateRootSlice, DaffCartReducersState, @@ -18,9 +17,8 @@ import { export interface DaffCartFeatureMemoizedSelectors< T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem > { - selectCartFeatureState: MemoizedSelector, DaffCartReducersState>; + selectCartFeatureState: MemoizedSelector, DaffCartReducersState>; } export const getDaffCartFeatureSelector = (() => { @@ -28,11 +26,10 @@ export const getDaffCartFeatureSelector = (() => { return < T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem - >(): DaffCartFeatureMemoizedSelectors => cache = cache + >(): DaffCartFeatureMemoizedSelectors => cache = cache ? cache : { selectCartFeatureState: - createFeatureSelector, DaffCartReducersState>(DAFF_CART_STORE_FEATURE_KEY), + createFeatureSelector, DaffCartReducersState>(DAFF_CART_STORE_FEATURE_KEY), }; })(); diff --git a/libs/cart/state/src/selectors/cart-item-entities/cart-item-entities.selectors.spec.ts b/libs/cart/state/src/selectors/cart-item-entities/cart-item-entities.selectors.spec.ts index a20970b2ba..802c482ea4 100644 --- a/libs/cart/state/src/selectors/cart-item-entities/cart-item-entities.selectors.spec.ts +++ b/libs/cart/state/src/selectors/cart-item-entities/cart-item-entities.selectors.spec.ts @@ -7,21 +7,22 @@ import { } from '@ngrx/store'; import { cold } from 'jasmine-marbles'; -import { DaffCart } from '@daffodil/cart'; +import { + DaffCart, + DaffCartItem, + DaffCompositeCartItem, + DaffConfigurableCartItem, +} from '@daffodil/cart'; import { DaffCartStateRootSlice, daffCartReducers, DaffCartItemListSuccess, DAFF_CART_STORE_FEATURE_KEY, - DaffStatefulCartItem, - DaffStatefulConfigurableCartItem, - DaffStatefulCompositeCartItem, DaffCartShippingMethodsLoad, DaffCartItemUpdate, DaffCartReducersState, daffCartItemEntitiesRetrievalActionsReducerFactory, daffCartRetrievalActionsReducerFactory, - DaffCartItemUpdateFailure, daffCartRetrivalActions, } from '@daffodil/cart/state'; import { @@ -31,7 +32,7 @@ import { } from '@daffodil/cart/state/testing'; import { DaffCartFactory } from '@daffodil/cart/testing'; import { - DaffStateError, + DaffOperationEntity, daffComposeReducers, daffIdentityReducer, } from '@daffodil/core/state'; @@ -45,9 +46,9 @@ describe('@daffodil/cart/state | getDaffCartItemEntitiesSelectors', () => { let statefulConfigurableCartItemFactory: DaffStatefulConfigurableCartItemFactory; let statefulCompositeCartItemFactory: DaffStatefulCompositeCartItemFactory; let mockCart: DaffCart; - let mockCartItems: DaffStatefulCartItem[]; - let mockStatefulConfigurableCartItems: DaffStatefulConfigurableCartItem[]; - let mockStatefulCompositeCartItems: DaffStatefulCompositeCartItem[]; + let mockCartItems: DaffOperationEntity[]; + let mockStatefulConfigurableCartItems: DaffOperationEntity[]; + let mockStatefulCompositeCartItems: DaffOperationEntity[]; const { selectCartItemIds, selectCartItemEntities, @@ -61,13 +62,6 @@ describe('@daffodil/cart/state | getDaffCartItemEntitiesSelectors', () => { selectOutOfStockCartItems, selectInStockCartItems, selectCartItemMutating, - selectCartItemState, - selectCartItemPrice, - selectCartItemRowTotal, - selectCartItemQuantity, - selectCartItemDiscounts, - selectCartItemTotalDiscount, - selectCartItemErrors, } = getDaffCartItemEntitiesSelectors(); beforeEach(() => { @@ -93,9 +87,15 @@ describe('@daffodil/cart/state | getDaffCartItemEntitiesSelectors', () => { statefulCompositeCartItemFactory = TestBed.inject(DaffStatefulCompositeCartItemFactory); mockCart = cartFactory.create(); - mockCartItems = statefulCartItemFactory.createMany(2); - mockStatefulConfigurableCartItems = statefulConfigurableCartItemFactory.createMany(2); - mockStatefulCompositeCartItems = statefulCompositeCartItemFactory.createMany(2); + mockCartItems = statefulCartItemFactory.createMany(2, { + daffState: jasmine.anything(), + }); + mockStatefulConfigurableCartItems = statefulConfigurableCartItemFactory.createMany(2, { + daffState: jasmine.anything(), + }); + mockStatefulCompositeCartItems = statefulCompositeCartItemFactory.createMany(2, { + daffState: jasmine.anything(), + }); store.dispatch(new DaffCartItemListSuccess(mockCartItems)); }); @@ -152,7 +152,7 @@ describe('@daffodil/cart/state | getDaffCartItemEntitiesSelectors', () => { it('should select the product of the given id', () => { const selector = store.pipe(select(selectCartItem(mockCartItems[0].id))); - const expected = cold('a', { a: mockCartItems[0] }); + const expected = cold('a', { a: jasmine.objectContaining({ id: mockCartItems[0].id }) }); expect(selector).toBeObservable(expected); }); @@ -267,15 +267,17 @@ describe('@daffodil/cart/state | getDaffCartItemEntitiesSelectors', () => { }); describe('selectOutOfStockCartItems', () => { - let inStockItem: DaffStatefulCartItem; - let outOfStockItem: DaffStatefulCartItem; + let inStockItem: DaffOperationEntity; + let outOfStockItem: DaffOperationEntity; beforeEach(() => { inStockItem = statefulCartItemFactory.create({ in_stock: true, + daffState: jasmine.anything(), }); outOfStockItem = statefulCartItemFactory.create({ in_stock: false, + daffState: jasmine.anything(), }); }); @@ -308,15 +310,17 @@ describe('@daffodil/cart/state | getDaffCartItemEntitiesSelectors', () => { }); describe('selectInStockCartItems', () => { - let inStockItem: DaffStatefulCartItem; - let outOfStockItem: DaffStatefulCartItem; + let inStockItem: DaffOperationEntity; + let outOfStockItem: DaffOperationEntity; beforeEach(() => { inStockItem = statefulCartItemFactory.create({ in_stock: true, + daffState: jasmine.anything(), }); outOfStockItem = statefulCartItemFactory.create({ in_stock: false, + daffState: jasmine.anything(), }); }); @@ -366,216 +370,4 @@ describe('@daffodil/cart/state | getDaffCartItemEntitiesSelectors', () => { expect(selector).toBeObservable(expected); }); }); - - describe('selectCartItemState', () => { - - it('should return the state of the cart item', () => { - store.dispatch(new DaffCartItemListSuccess(mockCartItems)); - const selector = store.pipe(select(selectCartItemState(mockCartItems[0].id))); - const expected = cold('a', { a: mockCartItems[0].daffState }); - - expect(selector).toBeObservable(expected); - }); - - it('should return null if the cart item is not in state', () => { - const selector = store.pipe(select(selectCartItemState(mockCartItems[0].id + 'notId'))); - const expected = cold('a', { a: null }); - - expect(selector).toBeObservable(expected); - }); - - it('should not emit when an unrelated piece of state changes', () => { - const spy = jasmine.createSpy(); - const selector = store.pipe(select(selectCartItemState(mockCartItems[0].id))); - - selector.subscribe(spy); - - store.dispatch(new DaffCartShippingMethodsLoad()); - - expect(spy).toHaveBeenCalledTimes(1); - }); - }); - - describe('selectCartItemPrice', () => { - - it('should return the price of the cart item', () => { - store.dispatch(new DaffCartItemListSuccess(mockCartItems)); - const selector = store.pipe(select(selectCartItemPrice(mockCartItems[0].id))); - const expected = cold('a', { a: mockCartItems[0].price }); - - expect(selector).toBeObservable(expected); - }); - - it('should return 0 if the cart item is not in state', () => { - const selector = store.pipe(select(selectCartItemPrice(mockCartItems[0].id + 'notId'))); - const expected = cold('a', { a: 0 }); - - expect(selector).toBeObservable(expected); - }); - - it('should not emit when an unrelated piece of state changes', () => { - const spy = jasmine.createSpy(); - const selector = store.pipe(select(selectCartItemPrice(mockCartItems[0].id))); - - selector.subscribe(spy); - - store.dispatch(new DaffCartShippingMethodsLoad()); - - expect(spy).toHaveBeenCalledTimes(1); - }); - }); - - describe('selectCartItemQuantity', () => { - - it('should return the quantity of the cart item', () => { - store.dispatch(new DaffCartItemListSuccess(mockCartItems)); - const selector = store.pipe(select(selectCartItemQuantity(mockCartItems[0].id))); - const expected = cold('a', { a: mockCartItems[0].qty }); - - expect(selector).toBeObservable(expected); - }); - - it('should return 0 if the cart item is not in state', () => { - const selector = store.pipe(select(selectCartItemQuantity(mockCartItems[0].id + 'notId'))); - const expected = cold('a', { a: 0 }); - - expect(selector).toBeObservable(expected); - }); - - it('should not emit when an unrelated piece of state changes', () => { - const spy = jasmine.createSpy(); - const selector = store.pipe(select(selectCartItemQuantity(mockCartItems[0].id))); - - selector.subscribe(spy); - - store.dispatch(new DaffCartShippingMethodsLoad()); - - expect(spy).toHaveBeenCalledTimes(1); - }); - }); - - describe('selectCartItemRowTotal', () => { - - it('should return the row total of the cart item', () => { - store.dispatch(new DaffCartItemListSuccess(mockCartItems)); - const selector = store.pipe(select(selectCartItemRowTotal(mockCartItems[0].id))); - const expected = cold('a', { a: mockCartItems[0].row_total }); - - expect(selector).toBeObservable(expected); - }); - - it('should return 0 if the cart item is not in state', () => { - const selector = store.pipe(select(selectCartItemRowTotal(mockCartItems[0].id + 'notId'))); - const expected = cold('a', { a: 0 }); - - expect(selector).toBeObservable(expected); - }); - - it('should not emit when an unrelated piece of state changes', () => { - const spy = jasmine.createSpy(); - const selector = store.pipe(select(selectCartItemRowTotal(mockCartItems[0].id))); - - selector.subscribe(spy); - - store.dispatch(new DaffCartShippingMethodsLoad()); - - expect(spy).toHaveBeenCalledTimes(1); - }); - }); - - describe('selectCartItemDiscounts', () => { - - it('should return the discounts of the cart item', () => { - store.dispatch(new DaffCartItemListSuccess(mockCartItems)); - const selector = store.pipe(select(selectCartItemDiscounts(mockCartItems[0].id))); - const expected = cold('a', { a: mockCartItems[0].discounts }); - - expect(selector).toBeObservable(expected); - }); - - it('should return an empty array if the cart item is not in state', () => { - const selector = store.pipe(select(selectCartItemDiscounts(mockCartItems[0].id + 'notId'))); - const expected = cold('a', { a: []}); - - expect(selector).toBeObservable(expected); - }); - - it('should not emit when an unrelated piece of state changes', () => { - const spy = jasmine.createSpy(); - const selector = store.pipe(select(selectCartItemDiscounts(mockCartItems[0].id))); - - selector.subscribe(spy); - - store.dispatch(new DaffCartShippingMethodsLoad()); - - expect(spy).toHaveBeenCalledTimes(1); - }); - }); - - describe('selectCartItemTotalDiscount', () => { - - it('should return the sum of all discounts of the cart item', () => { - store.dispatch(new DaffCartItemListSuccess(mockCartItems)); - const selector = store.pipe(select(selectCartItemTotalDiscount(mockCartItems[0].id))); - const expected = cold('a', { a: mockCartItems[0].discounts.reduce((acc, { amount }) => acc + amount, 0) }); - - expect(selector).toBeObservable(expected); - }); - - it('should return 0 if the cart item is not in state', () => { - const selector = store.pipe(select(selectCartItemTotalDiscount(mockCartItems[0].id + 'notId'))); - const expected = cold('a', { a: 0 }); - - expect(selector).toBeObservable(expected); - }); - - it('should not emit when an unrelated piece of state changes', () => { - const spy = jasmine.createSpy(); - const selector = store.pipe(select(selectCartItemTotalDiscount(mockCartItems[0].id))); - - selector.subscribe(spy); - - store.dispatch(new DaffCartShippingMethodsLoad()); - - expect(spy).toHaveBeenCalledTimes(1); - }); - }); - - describe('selectCartItemErrors', () => { - let error: DaffStateError; - - beforeEach(() => { - error = { - code: 'code', - message: 'message', - }; - }); - - it('should return the errors of the cart item', () => { - store.dispatch(new DaffCartItemListSuccess(mockCartItems)); - store.dispatch(new DaffCartItemUpdateFailure([error], mockCartItems[0].id)); - const selector = store.pipe(select(selectCartItemErrors(mockCartItems[0].id))); - const expected = cold('a', { a: [error]}); - - expect(selector).toBeObservable(expected); - }); - - it('should return an empty array if the cart item is not in state', () => { - const selector = store.pipe(select(selectCartItemErrors(mockCartItems[0].id + 'notId'))); - const expected = cold('a', { a: []}); - - expect(selector).toBeObservable(expected); - }); - - it('should not emit when an unrelated piece of state changes', () => { - const spy = jasmine.createSpy(); - const selector = store.pipe(select(selectCartItemErrors(mockCartItems[0].id))); - - selector.subscribe(spy); - - store.dispatch(new DaffCartShippingMethodsLoad()); - - expect(spy).toHaveBeenCalledTimes(1); - }); - }); }); diff --git a/libs/cart/state/src/selectors/cart-item-entities/cart-item-entities.selectors.ts b/libs/cart/state/src/selectors/cart-item-entities/cart-item-entities.selectors.ts index e5d2e5a4e5..97de809a64 100644 --- a/libs/cart/state/src/selectors/cart-item-entities/cart-item-entities.selectors.ts +++ b/libs/cart/state/src/selectors/cart-item-entities/cart-item-entities.selectors.ts @@ -1,4 +1,3 @@ -import { EntityState } from '@ngrx/entity'; import { createSelector, MemoizedSelector, @@ -11,19 +10,20 @@ import { DaffCart, DaffCartOrderResult, DaffCartItemInputType, - DaffConfigurableCartItem, DaffCompositeCartItem, - DaffCartItem, DaffCartItemDiscount, + DaffConfigurableCartItem, } from '@daffodil/cart'; import { daffAdd } from '@daffodil/core'; -import { DaffStateError } from '@daffodil/core/state'; - import { - DaffCartItemStateEnum, - DaffStatefulCartItem, -} from '../../models/stateful-cart-item'; -import { daffCartItemEntitiesAdapter } from '../../reducers/cart-item-entities/cart-item-entities-reducer-adapter'; + DaffOperationEntity, + DaffOperationEntityState, + DaffState, + DaffStateError, + daffOperationStateSelectorFactory, +} from '@daffodil/core/state'; + +import { daffCartItemEntitiesAdapter } from '../../reducers/cart-item-entities/adapter'; import { DaffCartStateRootSlice, DaffCartReducersState, @@ -33,79 +33,53 @@ import { getDaffCartFeatureSelector } from '../cart-feature.selector'; export interface DaffCartItemEntitiesMemoizedSelectors< T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem > { - selectCartItemEntitiesState: MemoizedSelector, EntityState>; - selectCartItemIds: MemoizedSelector, EntityState['ids']>; - selectCartItemEntities: MemoizedSelector, EntityState['entities']>; - selectAllCartItems: MemoizedSelector, U[]>; - selectCartItemTotal: MemoizedSelector, number>; - selectCartItem: (id: U['id']) => MemoizedSelector, U>; - selectTotalNumberOfCartItems: MemoizedSelector, number>; - selectCartItemConfiguredAttributes: (id: U['id']) => MemoizedSelector, DaffConfigurableCartItemAttribute[]>; - selectCartItemCompositeOptions: (id: U['id']) => MemoizedSelector, DaffCompositeCartItemOption[]>; - selectIsCartItemOutOfStock: (id: U['id']) => MemoizedSelector, boolean>; + selectCartItemEntitiesState: MemoizedSelector, DaffOperationEntityState>; + selectCartItemIds: MemoizedSelector, DaffOperationEntityState['ids']>; + selectCartItemEntities: MemoizedSelector, DaffOperationEntityState['entities']>; + selectAllCartItems: MemoizedSelector, DaffOperationEntity[]>; + selectCartItemTotal: MemoizedSelector, number>; + selectCartItem: (id: T['items'][number]['id']) => MemoizedSelector, DaffOperationEntity>; + selectTotalNumberOfCartItems: MemoizedSelector, number>; + selectCartItemConfiguredAttributes: (id: T['items'][number]['id']) => MemoizedSelector, DaffConfigurableCartItemAttribute[]>; + selectCartItemCompositeOptions: (id: T['items'][number]['id']) => MemoizedSelector, DaffCompositeCartItemOption[]>; + selectIsCartItemOutOfStock: (id: T['items'][number]['id']) => MemoizedSelector, boolean>; /** * Selects all cart items that are out of stock. */ - selectOutOfStockCartItems: MemoizedSelector, U[]>; + selectOutOfStockCartItems: MemoizedSelector, DaffOperationEntity[]>; /** * Selects all cart items that are in stock. */ - selectInStockCartItems: MemoizedSelector, U[]>; - selectCartItemMutating: MemoizedSelector, boolean>; - selectCartItemState: (id: U['id']) => MemoizedSelector, DaffCartItemStateEnum>; - /** - * Selects the specified item's price. - * Zero by default. - * This includes any discounts and sales that apply to the product or category. - * This excludes cart discounts. - */ - selectCartItemPrice: (id: U['id']) => MemoizedSelector, number>; - /** - * Selects the specified item's quantity. - * Zero by default. - */ - selectCartItemQuantity: (id: U['id']) => MemoizedSelector, number>; - /** - * Selects the specified item's row total. - * Zero by default. - * This includes any discounts and sales that apply to the product or category. - * This excludes cart discounts. - */ - selectCartItemRowTotal: (id: U['id']) => MemoizedSelector, number>; - /** - * Selects the specified item's array of cart (not product) discounts for the entire row. - */ - selectCartItemDiscounts: (id: U['id']) => MemoizedSelector, DaffCartItemDiscount[]>; - /** - * Selects the specified item's sum of all cart (not product) discounts for the entire row. - * Zero by default. - */ - selectCartItemTotalDiscount: (id: U['id']) => MemoizedSelector, number>; - /** - * Selects the specified item's errors. - */ - selectCartItemErrors: (id: U['id']) => MemoizedSelector, DaffStateError[]>; + selectInStockCartItems: MemoizedSelector, DaffOperationEntity[]>; + selectCartItemMutating: MemoizedSelector, boolean>; } const createCartItemEntitiesSelectors = < T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem ->(): DaffCartItemEntitiesMemoizedSelectors => { + Configurable extends DaffConfigurableCartItem = DaffConfigurableCartItem, + Composite extends DaffCompositeCartItem = DaffCompositeCartItem, +>(): DaffCartItemEntitiesMemoizedSelectors => { const { selectCartFeatureState, - } = getDaffCartFeatureSelector(); - const adapterSelectors = daffCartItemEntitiesAdapter().getSelectors(); + } = getDaffCartFeatureSelector(); /** * CartItem Entities State */ const selectCartItemEntitiesState = createSelector( selectCartFeatureState, - (state: DaffCartReducersState) => state.cartItems, + (state: DaffCartReducersState) => state.cartItems, ); + const { + selectOptimisticList: selectAllCartItems, + selectEntity: selectCartItem, + selectIds, + selectEntities, + selectTotal, + } = daffCartItemEntitiesAdapter().getSelectors(selectCartItemEntitiesState); + /** * Adapters created with @ngrx/entity generate * commonly used selector functions including @@ -118,39 +92,26 @@ const createCartItemEntitiesSelectors = < * Selector for product IDs. */ const selectCartItemIds = createSelector( - selectCartItemEntitiesState, - adapterSelectors.selectIds, + (s) => s, + selectIds, ); /** * Selector for all product entities (see ngrx/entity). */ const selectCartItemEntities = createSelector( - selectCartItemEntitiesState, - adapterSelectors.selectEntities, - ); - - /** - * Selector for all products on state. - */ - const selectAllCartItems = createSelector( - selectCartItemEntitiesState, - adapterSelectors.selectAll, + (s) => s, + selectEntities, ); /** * Selector for the total number of products. */ const selectCartItemTotal = createSelector( - selectCartItemEntitiesState, - adapterSelectors.selectTotal, + (s) => s, + selectTotal, ); - const selectCartItem = defaultMemoize((itemId: U['id']) => createSelector( - selectCartItemEntities, - cartItems => cartItems[itemId], - )).memoized; - /** * Selector for the total number of cart items that takes into account the qty on each cart item. */ @@ -159,31 +120,31 @@ const createCartItemEntitiesSelectors = < (cartItems) => cartItems.reduce((acc, cartItem) => acc + cartItem.qty, 0), ); - const selectCartItemConfiguredAttributes = defaultMemoize((itemId: U['id']) => createSelector( + const selectCartItemConfiguredAttributes = defaultMemoize((itemId: T['items'][number]['id']) => createSelector( selectCartItem(itemId), - (cartItem: DaffConfigurableCartItem) => { - if(cartItem.type !== DaffCartItemInputType.Configurable) { + (cartItem) => { + if (cartItem.type !== DaffCartItemInputType.Configurable) { return null; } - return cartItem.attributes; + return (>cartItem).attributes; }, )).memoized; - const selectCartItemCompositeOptions = defaultMemoize((itemId: U['id']) => createSelector( + const selectCartItemCompositeOptions = defaultMemoize((itemId: T['items'][number]['id']) => createSelector( selectCartItem(itemId), - (cartItem: DaffCompositeCartItem) => { - if(cartItem.type !== DaffCartItemInputType.Composite) { + (cartItem) => { + if (cartItem.type !== DaffCartItemInputType.Composite) { return null; } - return cartItem.options; + return (>cartItem).options; }, )).memoized; - const selectIsCartItemOutOfStock = defaultMemoize((itemId: U['id']) => createSelector( + const selectIsCartItemOutOfStock = defaultMemoize((itemId: T['items'][number]['id']) => createSelector( selectCartItem(itemId), - (cartItem: U) => cartItem ? !cartItem.in_stock : null, + (cartItem) => cartItem ? !cartItem.in_stock : null, )).memoized; const selectOutOfStockCartItems = createSelector( @@ -198,45 +159,10 @@ const createCartItemEntitiesSelectors = < const selectCartItemMutating = createSelector( selectAllCartItems, - (cartItems: U[]) => cartItems?.reduce((acc, item) => - acc || item.daffState === DaffCartItemStateEnum.Mutating, false), + (cartItems) => cartItems?.reduce((acc, item) => + acc || item.daffState === DaffState.Mutating, false), ); - const selectCartItemState = defaultMemoize((itemId: U['id']) => createSelector( - selectCartItem(itemId), - (cartItem: U) => cartItem?.daffState || null, - )).memoized; - - const selectCartItemQuantity = defaultMemoize((itemId: U['id']) => createSelector( - selectCartItem(itemId), - (cartItem: U) => cartItem?.qty || 0, - )).memoized; - - const selectCartItemRowTotal = defaultMemoize((itemId: U['id']) => createSelector( - selectCartItem(itemId), - (cartItem: U) => cartItem?.row_total || 0, - )).memoized; - - const selectCartItemPrice = defaultMemoize((itemId: U['id']) => createSelector( - selectCartItem(itemId), - (cartItem: U) => cartItem?.price || 0, - )).memoized; - - const selectCartItemDiscounts = defaultMemoize((itemId: U['id']) => createSelector( - selectCartItem(itemId), - (cartItem: U) => cartItem?.discounts || [], - )).memoized; - - const selectCartItemTotalDiscount = defaultMemoize((itemId: U['id']) => createSelector( - selectCartItemDiscounts(itemId), - (discounts: U['discounts']) => discounts?.reduce((acc, { amount }) => daffAdd(acc, amount), 0) || 0, - )).memoized; - - const selectCartItemErrors = defaultMemoize((itemId: U['id']) => createSelector( - selectCartItem(itemId), - (cartItem: U) => cartItem?.daffErrors || [], - )).memoized; - return { selectCartItemEntitiesState, selectCartItemIds, @@ -251,13 +177,6 @@ const createCartItemEntitiesSelectors = < selectInStockCartItems, selectOutOfStockCartItems, selectCartItemMutating, - selectCartItemState, - selectCartItemPrice, - selectCartItemRowTotal, - selectCartItemQuantity, - selectCartItemDiscounts, - selectCartItemTotalDiscount, - selectCartItemErrors, }; }; @@ -266,8 +185,7 @@ export const getDaffCartItemEntitiesSelectors = (() => { return < T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem - >(): DaffCartItemEntitiesMemoizedSelectors => cache = cache + >(): DaffCartItemEntitiesMemoizedSelectors => cache = cache ? cache - : createCartItemEntitiesSelectors(); + : createCartItemEntitiesSelectors(); })(); diff --git a/libs/cart/state/src/selectors/cart-order/cart-order.selector.ts b/libs/cart/state/src/selectors/cart-order/cart-order.selector.ts index 60939c9c8e..3399b17a17 100644 --- a/libs/cart/state/src/selectors/cart-order/cart-order.selector.ts +++ b/libs/cart/state/src/selectors/cart-order/cart-order.selector.ts @@ -9,7 +9,6 @@ import { } from '@daffodil/cart'; import { DaffState } from '@daffodil/core/state'; -import { DaffStatefulCartItem } from '../../models/stateful-cart-item'; import { DaffCartReducersState, DaffCartOrderReducerState, @@ -20,34 +19,32 @@ import { getDaffCartFeatureSelector } from '../cart-feature.selector'; export interface DaffCartOrderMemoizedSelectors< T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem > { - selectCartOrderState: MemoizedSelector, DaffCartOrderReducerState>; + selectCartOrderState: MemoizedSelector, DaffCartOrderReducerState>; /** * Selects whether there is a cart order operation in progress. */ - selectCartOrderLoading: MemoizedSelector, boolean>; + selectCartOrderLoading: MemoizedSelector, boolean>; /** * Selects whether there is a place order operation in progress. */ - selectCartOrderMutating: MemoizedSelector, boolean>; - selectCartOrderErrors: MemoizedSelector, DaffCartOrderReducerState['errors']>; - selectCartOrderValue: MemoizedSelector, DaffCartOrderReducerState['cartOrderResult']>; - selectCartOrderId: MemoizedSelector, DaffCartOrderReducerState['cartOrderResult']['orderId']>; - selectCartOrderCartId: MemoizedSelector, DaffCartOrderReducerState['cartOrderResult']['cartId']>; - selectHasOrderResult: MemoizedSelector, boolean>; + selectCartOrderMutating: MemoizedSelector, boolean>; + selectCartOrderErrors: MemoizedSelector, DaffCartOrderReducerState['errors']>; + selectCartOrderValue: MemoizedSelector, DaffCartOrderReducerState['cartOrderResult']>; + selectCartOrderId: MemoizedSelector, DaffCartOrderReducerState['cartOrderResult']['orderId']>; + selectCartOrderCartId: MemoizedSelector, DaffCartOrderReducerState['cartOrderResult']['cartId']>; + selectHasOrderResult: MemoizedSelector, boolean>; } const createCartOrderSelectors = < T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem ->(): DaffCartOrderMemoizedSelectors => { - const selectCartFeatureState = getDaffCartFeatureSelector().selectCartFeatureState; +>(): DaffCartOrderMemoizedSelectors => { + const selectCartFeatureState = getDaffCartFeatureSelector().selectCartFeatureState; const selectCartOrderState = createSelector( selectCartFeatureState, - (state: DaffCartReducersState) => state.order, + (state: DaffCartReducersState) => state.order, ); const selectCartOrderValue = createSelector( selectCartOrderState, @@ -97,8 +94,7 @@ export const getCartOrderSelectors = (() => { return < T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem - >(): DaffCartOrderMemoizedSelectors => cache = cache + >(): DaffCartOrderMemoizedSelectors => cache = cache ? cache - : createCartOrderSelectors(); + : createCartOrderSelectors(); })(); diff --git a/libs/cart/state/src/selectors/cart.selector.ts b/libs/cart/state/src/selectors/cart.selector.ts index b3ad282fc3..5ce91abd63 100644 --- a/libs/cart/state/src/selectors/cart.selector.ts +++ b/libs/cart/state/src/selectors/cart.selector.ts @@ -19,26 +19,23 @@ import { DaffCartOrderMemoizedSelectors, getCartOrderSelectors, } from './cart-order/cart-order.selector'; -import { DaffStatefulCartItem } from '../models/stateful-cart-item'; export interface DaffCartMemoizedSelectors< T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem > extends DaffCartFeatureMemoizedSelectors, - DaffCartOrderMemoizedSelectors, + DaffCartOrderMemoizedSelectors, DaffCartStateMemoizedSelectors, - DaffCartItemEntitiesMemoizedSelectors {} + DaffCartItemEntitiesMemoizedSelectors {} const createCartSelectors = < T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem ->(): DaffCartMemoizedSelectors => ({ - ...getDaffCartFeatureSelector(), - ...getCartOrderSelectors(), - ...getCartSelectors(), - ...getDaffCartItemEntitiesSelectors(), +>(): DaffCartMemoizedSelectors => ({ + ...getDaffCartFeatureSelector(), + ...getCartOrderSelectors(), + ...getCartSelectors(), + ...getDaffCartItemEntitiesSelectors(), }); export const getDaffCartSelectors = (() => { @@ -46,8 +43,7 @@ export const getDaffCartSelectors = (() => { return < T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem - >(): DaffCartMemoizedSelectors => cache = cache + >(): DaffCartMemoizedSelectors => cache = cache ? cache - : createCartSelectors(); + : createCartSelectors(); })(); diff --git a/libs/cart/state/src/selectors/cart/cart.selector.spec.ts b/libs/cart/state/src/selectors/cart/cart.selector.spec.ts index 61a6251861..745ab88c69 100644 --- a/libs/cart/state/src/selectors/cart/cart.selector.spec.ts +++ b/libs/cart/state/src/selectors/cart/cart.selector.spec.ts @@ -121,26 +121,7 @@ describe('@daffodil/cart/state | getCartSelectors', () => { selectPaymentMethodsErrors, selectCouponErrors, - selectCartId, - selectCartSubtotal, - selectCartGrandTotal, - selectCartSubtotalExcludingTax, - selectCartSubtotalIncludingTax, - selectCartSubtotalWithDiscountExcludingTax, - selectCartSubtotalWithDiscountIncludingTax, - selectCartTotalTax, - selectCartDiscountTotals, - selectCartShippingTotal, - selectCartCoupons, - selectCartItems, selectCartHasOutOfStockItems, - selectCartBillingAddress, - selectCartShippingAddress, - selectCartPayment, - selectCartTotals, - selectCartShippingInformation, - selectCartAvailableShippingMethods, - selectCartAvailablePaymentMethods, selectIsCartEmpty, selectIsBillingSameAsShipping, @@ -1336,114 +1317,6 @@ describe('@daffodil/cart/state | getCartSelectors', () => { }); }); - describe('selectCartId', () => { - it('returns cart ID', () => { - const selector = store.pipe(select(selectCartId)); - const expected = cold('a', { a: cart.id }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartSubtotal', () => { - it('returns cart subtotal', () => { - const selector = store.pipe(select(selectCartSubtotal)); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.subtotalExcludingTax].value }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartGrandTotal', () => { - it('returns cart grand total', () => { - const selector = store.pipe(select(selectCartGrandTotal)); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.grandTotal].value }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartSubtotalExcludingTax', () => { - it('returns cart subtotal excluding tax', () => { - const selector = store.pipe(select(selectCartSubtotalExcludingTax)); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.subtotalExcludingTax].value }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartSubtotalIncludingTax', () => { - it('returns cart subtotal including tax', () => { - const selector = store.pipe(select(selectCartSubtotalIncludingTax)); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.subtotalIncludingTax].value }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartSubtotalWithDiscountExcludingTax', () => { - it('returns cart subtotal with discount excluding tax', () => { - const selector = store.pipe(select(selectCartSubtotalWithDiscountExcludingTax)); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.subtotalWithDiscountExcludingTax].value }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartSubtotalWithDiscountIncludingTax', () => { - it('returns cart subtotal with discount including tax', () => { - const selector = store.pipe(select(selectCartSubtotalWithDiscountIncludingTax)); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.subtotalWithDiscountIncludingTax].value }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartTotalTax', () => { - it('returns cart total tax', () => { - const selector = store.pipe(select(selectCartTotalTax)); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.tax].value }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartDiscountTotals', () => { - it('returns cart discount totals', () => { - const selector = store.pipe(select(selectCartDiscountTotals)); - const expected = cold('a', { a: [cart.totals[DaffCartTotalTypeEnum.discount]]}); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartShippingTotal', () => { - it('returns cart shipping total', () => { - const selector = store.pipe(select(selectCartShippingTotal)); - const expected = cold('a', { a: cart.totals[DaffCartTotalTypeEnum.shipping].value }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartCoupons', () => { - it('returns cart coupons', () => { - const selector = store.pipe(select(selectCartCoupons)); - const expected = cold('a', { a: cart.coupons }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartItems', () => { - it('returns cart items', () => { - const selector = store.pipe(select(selectCartItems)); - const expected = cold('a', { a: cart.items }); - - expect(selector).toBeObservable(expected); - }); - }); - describe('selectCartHasOutOfStockItems', () => { it('should return true when at least one cart item is out of stock', () => { store.dispatch(new DaffCartLoadSuccess({ @@ -1467,69 +1340,6 @@ describe('@daffodil/cart/state | getCartSelectors', () => { }); }); - describe('selectCartBillingAddress', () => { - it('returns cart billing address', () => { - const selector = store.pipe(select(selectCartBillingAddress)); - const expected = cold('a', { a: cart.billing_address }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartShippingAddress', () => { - it('returns cart shipping address', () => { - const selector = store.pipe(select(selectCartShippingAddress)); - const expected = cold('a', { a: cart.shipping_address }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartPayment', () => { - it('returns cart payment', () => { - const selector = store.pipe(select(selectCartPayment)); - const expected = cold('a', { a: cart.payment }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartTotals', () => { - it('returns cart totals', () => { - const selector = store.pipe(select(selectCartTotals)); - const expected = cold('a', { a: cart.totals }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartShippingInformation', () => { - it('returns cart shipping information', () => { - const selector = store.pipe(select(selectCartShippingInformation)); - const expected = cold('a', { a: cart.shipping_information }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartAvailableShippingMethods', () => { - it('returns cart available shipping methods', () => { - const selector = store.pipe(select(selectCartAvailableShippingMethods)); - const expected = cold('a', { a: cart.available_shipping_methods }); - - expect(selector).toBeObservable(expected); - }); - }); - - describe('selectCartAvailablePaymentMethods', () => { - it('returns cart available payment methods', () => { - const selector = store.pipe(select(selectCartAvailablePaymentMethods)); - const expected = cold('a', { a: cart.available_payment_methods }); - - expect(selector).toBeObservable(expected); - }); - }); - describe('selectIsCartEmpty', () => { it('selects whether the cart is empty', () => { const selector = store.pipe(select(selectIsCartEmpty)); diff --git a/libs/cart/state/src/selectors/cart/cart.selector.ts b/libs/cart/state/src/selectors/cart/cart.selector.ts index be11159252..7b0f2a7648 100644 --- a/libs/cart/state/src/selectors/cart/cart.selector.ts +++ b/libs/cart/state/src/selectors/cart/cart.selector.ts @@ -16,9 +16,7 @@ import { } from '@daffodil/core/state'; import { daffComparePersonalAddresses } from '@daffodil/geography'; -import { DaffStatefulCartItem } from '../../models/stateful-cart-item'; import { DaffCartResolveState } from '../../reducers/cart-resolve/cart-resolve-state.enum'; -import { DaffCartItemLoadingState } from '../../reducers/loading/cart-loading.type'; import { DaffCartReducerState, DaffCartReducersState, @@ -31,216 +29,194 @@ import { getDaffCartItemEntitiesSelectors } from '../cart-item-entities/cart-ite export interface DaffCartStateMemoizedSelectors< T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem > { - selectCartState: MemoizedSelector, DaffCartReducerState>; - selectCartValue: MemoizedSelector, T>; + selectCartState: MemoizedSelector, DaffCartReducerState>; + selectCartValue: MemoizedSelector, T>; - selectCartResolved: MemoizedSelector, DaffCartResolveState>; + selectCartResolved: MemoizedSelector, DaffCartResolveState>; /** * The object that holds all the loading states for cart operations. */ - selectCartLoadingObject: MemoizedSelector, DaffCartReducerState['loading']>; + selectCartLoadingObject: MemoizedSelector, DaffCartReducerState['loading']>; /** * Selects whether there is any cart operation in progress. * This includes operations specifically for cart subfields. */ - selectCartFeatureLoading: MemoizedSelector, boolean>; + selectCartFeatureLoading: MemoizedSelector, boolean>; /** * Selects whether there is any cart resolve operation in progress. * This includes operations for cart subfields. * This pertains only to requests that do not mutate data such as "load" or "list". */ - selectCartFeatureResolving: MemoizedSelector, boolean>; + selectCartFeatureResolving: MemoizedSelector, boolean>; /** * Selects whether there is any cart mutate operation in progress. * This includes operations for cart subfields. * This pertains only to requests that mutate data such as "update". */ - selectCartFeatureMutating: MemoizedSelector, boolean>; + selectCartFeatureMutating: MemoizedSelector, boolean>; /** * Selects whether there is a cart operation in progress. * This does not include operations specifically for cart subfields. */ - selectCartLoading: MemoizedSelector, boolean>; + selectCartLoading: MemoizedSelector, boolean>; /** * Selects whether there is a cart resolve operation in progress. * This does not include operations specifically for cart subfields. * This pertains only to requests that do not mutate data such as "load" or "list". */ - selectCartResolving: MemoizedSelector, boolean>; + selectCartResolving: MemoizedSelector, boolean>; /** * Selects whether there is a cart mutate operation in progress. * This does not include operations specifically for cart subfields. * This pertains only to requests that mutate data such as "update". */ - selectCartMutating: MemoizedSelector, boolean>; + selectCartMutating: MemoizedSelector, boolean>; /** * Selects whether there is a cart billing address operation in progress. */ - selectBillingAddressLoading: MemoizedSelector, boolean>; + selectBillingAddressLoading: MemoizedSelector, boolean>; /** * Selects whether there is a cart billing address resolve operation in progress. * This pertains only to requests that do not mutate data such as "load" or "list". */ - selectBillingAddressResolving: MemoizedSelector, boolean>; + selectBillingAddressResolving: MemoizedSelector, boolean>; /** * Selects whether there is a cart billing address mutate operation in progress. * This pertains only to requests that mutate data such as "update". */ - selectBillingAddressMutating: MemoizedSelector, boolean>; + selectBillingAddressMutating: MemoizedSelector, boolean>; /** * Selects whether there is a cart shipping address operation in progress. */ - selectShippingAddressLoading: MemoizedSelector, boolean>; + selectShippingAddressLoading: MemoizedSelector, boolean>; /** * Selects whether there is a cart shipping address resolve operation in progress. * This pertains only to requests that do not mutate data such as "load" or "list". */ - selectShippingAddressResolving: MemoizedSelector, boolean>; + selectShippingAddressResolving: MemoizedSelector, boolean>; /** * Selects whether there is a cart shipping address mutate operation in progress. * This pertains only to requests that mutate data such as "update". */ - selectShippingAddressMutating: MemoizedSelector, boolean>; + selectShippingAddressMutating: MemoizedSelector, boolean>; /** * Selects whether there is a cart shipping information operation in progress. */ - selectShippingInformationLoading: MemoizedSelector, boolean>; + selectShippingInformationLoading: MemoizedSelector, boolean>; /** * Selects whether there is a cart shipping information resolve operation in progress. * This pertains only to requests that do not mutate data such as "load" or "list". */ - selectShippingInformationResolving: MemoizedSelector, boolean>; + selectShippingInformationResolving: MemoizedSelector, boolean>; /** * Selects whether there is a cart shipping information mutate operation in progress. * This pertains only to requests that mutate data such as "update". */ - selectShippingInformationMutating: MemoizedSelector, boolean>; + selectShippingInformationMutating: MemoizedSelector, boolean>; /** * Selects whether there is a cart shipping methods operation in progress. */ - selectShippingMethodsLoading: MemoizedSelector, boolean>; + selectShippingMethodsLoading: MemoizedSelector, boolean>; /** * Selects whether there is a cart shipping methods resolve operation in progress. * This pertains only to requests that do not mutate data such as "load" or "list". */ - selectShippingMethodsResolving: MemoizedSelector, boolean>; + selectShippingMethodsResolving: MemoizedSelector, boolean>; /** * Selects whether there is a cart payment operation in progress. */ - selectPaymentLoading: MemoizedSelector, boolean>; + selectPaymentLoading: MemoizedSelector, boolean>; /** * Selects whether there is a cart payment resolve operation in progress. * This pertains only to requests that do not mutate data such as "load" or "list". */ - selectPaymentResolving: MemoizedSelector, boolean>; + selectPaymentResolving: MemoizedSelector, boolean>; /** * Selects whether there is a cart payment mutate operation in progress. * This pertains only to requests that mutate data such as "update". */ - selectPaymentMutating: MemoizedSelector, boolean>; + selectPaymentMutating: MemoizedSelector, boolean>; /** * Selects whether there is a cart payment methods operation in progress. */ - selectPaymentMethodsLoading: MemoizedSelector, boolean>; + selectPaymentMethodsLoading: MemoizedSelector, boolean>; /** * Selects whether there is a cart payment methods resolve operation in progress. * This pertains only to requests that do not mutate data such as "load" or "list". */ - selectPaymentMethodsResolving: MemoizedSelector, boolean>; + selectPaymentMethodsResolving: MemoizedSelector, boolean>; /** * Selects whether there is a cart coupon operation in progress. */ - selectCouponLoading: MemoizedSelector, boolean>; + selectCouponLoading: MemoizedSelector, boolean>; /** * Selects whether there is a cart coupon resolve operation in progress. * This pertains only to requests that do not mutate data such as "load" or "list". */ - selectCouponResolving: MemoizedSelector, boolean>; + selectCouponResolving: MemoizedSelector, boolean>; /** * Selects whether there is a cart coupon mutate operation in progress. * This pertains only to requests that mutate data such as "update". */ - selectCouponMutating: MemoizedSelector, boolean>; + selectCouponMutating: MemoizedSelector, boolean>; /** * Selects whether there is a cart item operation in progress. */ - selectItemLoading: MemoizedSelector, boolean>; + selectItemLoading: MemoizedSelector, boolean>; /** * Selects whether there is a cart item add operation in progress. */ - selectItemAdding: MemoizedSelector, boolean>; + selectItemAdding: MemoizedSelector, boolean>; /** * Selects whether there is a cart item resolve operation in progress. * This pertains only to requests that do not mutate data such as "load" or "list". */ - selectItemResolving: MemoizedSelector, boolean>; + selectItemResolving: MemoizedSelector, boolean>; - selectCartErrorsObject: MemoizedSelector, DaffCartReducerState['errors']>; - selectCartErrors: MemoizedSelector, DaffStateError[]>; - selectBillingAddressErrors: MemoizedSelector, DaffStateError[]>; - selectShippingAddressErrors: MemoizedSelector, DaffStateError[]>; - selectShippingInformationErrors: MemoizedSelector, DaffStateError[]>; - selectShippingMethodsErrors: MemoizedSelector, DaffStateError[]>; - selectPaymentErrors: MemoizedSelector, DaffStateError[]>; - selectPaymentMethodsErrors: MemoizedSelector, DaffStateError[]>; - selectCouponErrors: MemoizedSelector, DaffStateError[]>; - selectItemErrors: MemoizedSelector, DaffStateError[]>; - - selectCartId: MemoizedSelector, T['id']>; - selectCartSubtotal: MemoizedSelector, DaffCartTotal['value']>; - selectCartGrandTotal: MemoizedSelector, DaffCartTotal['value']>; - selectCartSubtotalExcludingTax: MemoizedSelector, DaffCartTotal['value']>; - selectCartSubtotalIncludingTax: MemoizedSelector, DaffCartTotal['value']>; - selectCartSubtotalWithDiscountExcludingTax: MemoizedSelector, DaffCartTotal['value']>; - selectCartSubtotalWithDiscountIncludingTax: MemoizedSelector, DaffCartTotal['value']>; - selectCartTotalTax: MemoizedSelector, DaffCartTotal['value']>; - /** - * Selects the DaffCartTotals for cart discounts. These are discounts associated with coupon codes. - * @deprecated use {@link DaffCart#discounts} instead. - */ - selectCartDiscountTotals: MemoizedSelector, DaffCartTotal[]>; - selectCartShippingTotal: MemoizedSelector, DaffCartTotal['value']>; - selectCartCoupons: MemoizedSelector, T['coupons']>; /** - * @deprecated use getDaffCartItemEntitiesSelectors().selectAllCartItems instead. + * Selects whether there is a cart item mutate operation in progress. + * This pertains only to requests that mutate existing data such as update. + * This does not apply to add requests. see {@link selectItemAdding} for that. */ - selectCartItems: MemoizedSelector, T['items']>; - selectCartHasOutOfStockItems: MemoizedSelector, boolean>; - selectCartBillingAddress: MemoizedSelector, T['billing_address']>; - selectCartShippingAddress: MemoizedSelector, T['shipping_address']>; - selectCartPayment: MemoizedSelector, T['payment']>; - selectCartTotals: MemoizedSelector, T['totals']>; - selectCartShippingInformation: MemoizedSelector, T['shipping_information']>; - selectCartAvailableShippingMethods: MemoizedSelector, T['available_shipping_methods']>; - selectCartAvailablePaymentMethods: MemoizedSelector, T['available_payment_methods']>; + selectItemMutating: MemoizedSelector, boolean>; - selectIsCartEmpty: MemoizedSelector, boolean>; + selectCartErrorsObject: MemoizedSelector, DaffCartReducerState['errors']>; + selectCartErrors: MemoizedSelector, DaffStateError[]>; + selectBillingAddressErrors: MemoizedSelector, DaffStateError[]>; + selectShippingAddressErrors: MemoizedSelector, DaffStateError[]>; + selectShippingInformationErrors: MemoizedSelector, DaffStateError[]>; + selectShippingMethodsErrors: MemoizedSelector, DaffStateError[]>; + selectPaymentErrors: MemoizedSelector, DaffStateError[]>; + selectPaymentMethodsErrors: MemoizedSelector, DaffStateError[]>; + selectCouponErrors: MemoizedSelector, DaffStateError[]>; + selectItemErrors: MemoizedSelector, DaffStateError[]>; + + selectCartHasOutOfStockItems: MemoizedSelector, boolean>; + selectIsCartEmpty: MemoizedSelector, boolean>; /** * Selects whether the cart's shipping address equals the billing address. * Returns false if either address is null or undefined. */ - selectIsBillingSameAsShipping: MemoizedSelector, boolean>; + selectIsBillingSameAsShipping: MemoizedSelector, boolean>; - selectHasBillingAddress: MemoizedSelector, boolean>; - selectHasShippingAddress: MemoizedSelector, boolean>; - selectHasShippingMethod: MemoizedSelector, boolean>; - selectHasPaymentMethod: MemoizedSelector, boolean>; - selectCanPlaceOrder: MemoizedSelector, boolean, DefaultProjectorFn>; + selectHasBillingAddress: MemoizedSelector, boolean>; + selectHasShippingAddress: MemoizedSelector, boolean>; + selectHasShippingMethod: MemoizedSelector, boolean>; + selectHasPaymentMethod: MemoizedSelector, boolean>; + selectCanPlaceOrder: MemoizedSelector, boolean, DefaultProjectorFn>; } const createCartSelectors = < T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem ->(): DaffCartStateMemoizedSelectors => { - const selectCartFeatureState = getDaffCartFeatureSelector().selectCartFeatureState; - const { selectCartItemMutating } = getDaffCartItemEntitiesSelectors(); +>(): DaffCartStateMemoizedSelectors => { + const selectCartFeatureState = getDaffCartFeatureSelector().selectCartFeatureState; + const { selectCartItemMutating } = getDaffCartItemEntitiesSelectors(); const selectCartState = createSelector( selectCartFeatureState, - (state: DaffCartReducersState) => state.cart, + (state: DaffCartReducersState) => state.cart, ); const selectCartValue = createSelector( selectCartState, @@ -344,6 +320,10 @@ const createCartSelectors = < selectCartLoadingObject, loadingObject => loadingObject[DaffCartOperationType.Item] === DaffState.Resolving, ); + const selectItemMutating = createSelector( + selectCartLoadingObject, + loadingObject => loadingObject[DaffCartOperationType.Item] === DaffState.Mutating, + ); const selectCouponLoading = createSelector( selectCartLoadingObject, loadingObject => loadingObject[DaffCartOperationType.Coupon] !== DaffState.Complete, @@ -390,8 +370,7 @@ const createCartSelectors = < ); const selectCartFeatureMutating = createSelector( selectCartLoadingObject, - selectCartItemMutating, - (loadingObject, cartItemMutating) => [ + (loadingObject) => [ selectCartMutating, selectBillingAddressMutating, selectShippingAddressMutating, @@ -399,9 +378,10 @@ const createCartSelectors = < selectPaymentMutating, selectCouponMutating, selectItemAdding, + selectItemMutating, ].map(selector => selector.projector(loadingObject), - ).reduce((acc, mutating) => acc || mutating, false) || cartItemMutating, + ).reduce((acc, mutating) => acc || mutating, false), ); const selectCartErrorsObject = createSelector( @@ -445,84 +425,6 @@ const createCartSelectors = < (state: DaffCartReducerState['errors']) => state[DaffCartOperationType.Coupon], ); - const selectCartId = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => state.id, - ); - /** - * @deprecated use selectCartSubtotalExcludingTax selector instead. - */ - const selectCartSubtotal = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => { - const subtotalObject = state.totals[DaffCartTotalTypeEnum.subtotalExcludingTax]; - return subtotalObject ? subtotalObject.value : null; - }, - ); - const selectCartGrandTotal = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => { - const grandTotalObject = state.totals[DaffCartTotalTypeEnum.grandTotal]; - return grandTotalObject ? grandTotalObject.value : null; - }, - ); - const selectCartSubtotalExcludingTax = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => { - const subtotalExcludingTaxObject = state.totals[DaffCartTotalTypeEnum.subtotalExcludingTax]; - return subtotalExcludingTaxObject ? subtotalExcludingTaxObject.value : null; - }, - ); - const selectCartSubtotalIncludingTax = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => { - const subtotalIncludingTaxObject = state.totals[DaffCartTotalTypeEnum.subtotalIncludingTax]; - return subtotalIncludingTaxObject ? subtotalIncludingTaxObject.value : null; - }, - ); - const selectCartSubtotalWithDiscountExcludingTax = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => { - const subtotalWithDiscountExcludingTaxObject = state.totals[DaffCartTotalTypeEnum.subtotalWithDiscountExcludingTax]; - return subtotalWithDiscountExcludingTaxObject ? subtotalWithDiscountExcludingTaxObject.value : null; - }, - ); - const selectCartSubtotalWithDiscountIncludingTax = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => { - const subtotalWithDiscountIncludingTaxObject = state.totals[DaffCartTotalTypeEnum.subtotalWithDiscountIncludingTax]; - return subtotalWithDiscountIncludingTaxObject ? subtotalWithDiscountIncludingTaxObject.value : null; - }, - ); - const selectCartTotalTax = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => { - const taxObject = state.totals[DaffCartTotalTypeEnum.tax]; - return taxObject ? taxObject.value : null; - }, - ); - const selectCartDiscountTotals = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => { - const discount = state.totals[DaffCartTotalTypeEnum.discount]; - return discount ? [discount] : []; - }, - ); - const selectCartShippingTotal = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => { - const shippingTotalObject = state.totals[DaffCartTotalTypeEnum.shipping]; - return shippingTotalObject ? shippingTotalObject.value : null; - }, - ); - const selectCartCoupons = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => state.coupons, - ); - const selectCartItems = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => state.items, - ); const selectCartHasOutOfStockItems = createSelector( selectCartValue, (state: DaffCartReducerState['cart']) => state.items.reduce((acc, item) => (acc || !item.in_stock), false), @@ -539,22 +441,10 @@ const createCartSelectors = < selectCartValue, (state: DaffCartReducerState['cart']) => state.payment, ); - const selectCartTotals = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => state.totals, - ); const selectCartShippingInformation = createSelector( selectCartValue, (state: DaffCartReducerState['cart']) => state.shipping_information, ); - const selectCartAvailableShippingMethods = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => state.available_shipping_methods, - ); - const selectCartAvailablePaymentMethods = createSelector( - selectCartValue, - (state: DaffCartReducerState['cart']) => state.available_payment_methods, - ); const selectIsCartEmpty = createSelector( selectCartValue, @@ -643,6 +533,7 @@ const createCartSelectors = < selectItemLoading, selectItemAdding, selectItemResolving, + selectItemMutating, selectCartErrorsObject, selectCartErrors, @@ -655,26 +546,7 @@ const createCartSelectors = < selectItemErrors, selectCouponErrors, - selectCartId, - selectCartSubtotal, - selectCartGrandTotal, - selectCartSubtotalExcludingTax, - selectCartSubtotalIncludingTax, - selectCartSubtotalWithDiscountExcludingTax, - selectCartSubtotalWithDiscountIncludingTax, - selectCartDiscountTotals, - selectCartTotalTax, - selectCartShippingTotal, - selectCartCoupons, - selectCartItems, selectCartHasOutOfStockItems, - selectCartBillingAddress, - selectCartShippingAddress, - selectCartPayment, - selectCartTotals, - selectCartShippingInformation, - selectCartAvailableShippingMethods, - selectCartAvailablePaymentMethods, selectIsCartEmpty, selectIsBillingSameAsShipping, @@ -692,8 +564,7 @@ export const getCartSelectors = (() => { return < T extends DaffCart = DaffCart, V extends DaffCartOrderResult = DaffCartOrderResult, - U extends DaffStatefulCartItem = DaffStatefulCartItem >(): DaffCartStateMemoizedSelectors => cache = cache ? cache - : createCartSelectors(); + : createCartSelectors(); })(); diff --git a/libs/cart/state/testing/src/factories/stateful-cart-item.factory.spec.ts b/libs/cart/state/testing/src/factories/stateful-cart-item.factory.spec.ts index ea1ca27389..e78a906f39 100644 --- a/libs/cart/state/testing/src/factories/stateful-cart-item.factory.spec.ts +++ b/libs/cart/state/testing/src/factories/stateful-cart-item.factory.spec.ts @@ -1,6 +1,7 @@ import { TestBed } from '@angular/core/testing'; -import { DaffStatefulCartItem } from '@daffodil/cart/state'; +import { DaffCartItem } from '@daffodil/cart'; +import { DaffOperationEntity } from '@daffodil/core/state'; import { DaffStatefulCartItemFactory } from './stateful-cart-item.factory'; @@ -22,7 +23,7 @@ describe('Cart | State | Testing | Factories | StatefulCartItemFactory', () => { describe('create', () => { - let result: DaffStatefulCartItem; + let result: DaffOperationEntity; beforeEach(() => { result = statefulCartItemFactory.create(); diff --git a/libs/cart/state/testing/src/factories/stateful-cart-item.factory.ts b/libs/cart/state/testing/src/factories/stateful-cart-item.factory.ts index add8d61a00..bd52d9811d 100644 --- a/libs/cart/state/testing/src/factories/stateful-cart-item.factory.ts +++ b/libs/cart/state/testing/src/factories/stateful-cart-item.factory.ts @@ -1,24 +1,25 @@ import { Injectable } from '@angular/core'; import { faker } from '@faker-js/faker/locale/en_US'; -import { - DaffStatefulCartItem, - DaffCartItemStateEnum, -} from '@daffodil/cart/state'; +import { DaffCartItem } from '@daffodil/cart'; import { DaffMockCartItem } from '@daffodil/cart/testing'; +import { + DaffOperationEntity, + DaffState, +} from '@daffodil/core/state'; import { DaffModelFactory } from '@daffodil/core/testing'; -export class DaffMockStatefulCartItem extends DaffMockCartItem implements DaffStatefulCartItem { - daffState = DaffCartItemStateEnum.Default; +export class DaffMockStatefulCartItem extends DaffMockCartItem implements DaffOperationEntity { + daffState = DaffState.Stable; daffErrors = []; + daffTemp = false; } @Injectable({ providedIn: 'root', }) -export class DaffStatefulCartItemFactory extends DaffModelFactory { - - constructor(){ +export class DaffStatefulCartItemFactory extends DaffModelFactory> { + constructor() { super(DaffMockStatefulCartItem); } } diff --git a/libs/cart/state/testing/src/factories/stateful-composite-cart-item.factory.ts b/libs/cart/state/testing/src/factories/stateful-composite-cart-item.factory.ts index 4a6f7e3713..6f2fbcff86 100644 --- a/libs/cart/state/testing/src/factories/stateful-composite-cart-item.factory.ts +++ b/libs/cart/state/testing/src/factories/stateful-composite-cart-item.factory.ts @@ -1,24 +1,25 @@ import { Injectable } from '@angular/core'; import { faker } from '@faker-js/faker/locale/en_US'; -import { - DaffCartItemStateEnum, - DaffStatefulCompositeCartItem, -} from '@daffodil/cart/state'; +import { DaffCompositeCartItem } from '@daffodil/cart'; import { DaffMockCompositeCartItem } from '@daffodil/cart/testing'; +import { + DaffOperationEntity, + DaffState, +} from '@daffodil/core/state'; import { DaffModelFactory } from '@daffodil/core/testing'; -export class DaffMockStatefulCompositeCartItem extends DaffMockCompositeCartItem implements DaffStatefulCompositeCartItem { - daffState: DaffCartItemStateEnum.Default; +export class DaffMockStatefulCompositeCartItem extends DaffMockCompositeCartItem implements DaffOperationEntity { + daffState = DaffState.Stable; daffErrors = []; + daffTemp = false; } @Injectable({ providedIn: 'root', }) -export class DaffStatefulCompositeCartItemFactory extends DaffModelFactory { - - constructor(){ +export class DaffStatefulCompositeCartItemFactory extends DaffModelFactory> { + constructor() { super(DaffMockStatefulCompositeCartItem); } } diff --git a/libs/cart/state/testing/src/factories/stateful-composite-cart-item.spec.ts b/libs/cart/state/testing/src/factories/stateful-composite-cart-item.spec.ts index f8314dd774..a790cf5cb7 100644 --- a/libs/cart/state/testing/src/factories/stateful-composite-cart-item.spec.ts +++ b/libs/cart/state/testing/src/factories/stateful-composite-cart-item.spec.ts @@ -1,6 +1,7 @@ import { TestBed } from '@angular/core/testing'; -import { DaffStatefulCompositeCartItem } from '@daffodil/cart/state'; +import { DaffCompositeCartItem } from '@daffodil/cart'; +import { DaffOperationEntity } from '@daffodil/core/state'; import { DaffStatefulCompositeCartItemFactory } from './stateful-composite-cart-item.factory'; @@ -22,7 +23,7 @@ describe('Cart | State | Testing | Factories | StatefulCompositeCartItemFactory' describe('create', () => { - let result: DaffStatefulCompositeCartItem; + let result: DaffOperationEntity; beforeEach(() => { result = statefulCompositeCartItemFactory.create(); diff --git a/libs/cart/state/testing/src/factories/stateful-configurable-cart-item.factory.ts b/libs/cart/state/testing/src/factories/stateful-configurable-cart-item.factory.ts index 2dc5bf5790..6b2a9c9230 100644 --- a/libs/cart/state/testing/src/factories/stateful-configurable-cart-item.factory.ts +++ b/libs/cart/state/testing/src/factories/stateful-configurable-cart-item.factory.ts @@ -1,24 +1,25 @@ import { Injectable } from '@angular/core'; import { faker } from '@faker-js/faker/locale/en_US'; -import { - DaffCartItemStateEnum, - DaffStatefulConfigurableCartItem, -} from '@daffodil/cart/state'; +import { DaffConfigurableCartItem } from '@daffodil/cart'; import { DaffMockConfigurableCartItem } from '@daffodil/cart/testing'; +import { + DaffOperationEntity, + DaffState, +} from '@daffodil/core/state'; import { DaffModelFactory } from '@daffodil/core/testing'; -export class DaffMockStatefulConfigurableCartItem extends DaffMockConfigurableCartItem implements DaffStatefulConfigurableCartItem { - daffState: DaffCartItemStateEnum.Default; +export class DaffMockStatefulConfigurableCartItem extends DaffMockConfigurableCartItem implements DaffOperationEntity { + daffState = DaffState.Stable; daffErrors = []; + daffTemp = false; } @Injectable({ providedIn: 'root', }) -export class DaffStatefulConfigurableCartItemFactory extends DaffModelFactory { - - constructor(){ +export class DaffStatefulConfigurableCartItemFactory extends DaffModelFactory> { + constructor() { super(DaffMockStatefulConfigurableCartItem); } } diff --git a/libs/cart/state/testing/src/factories/stateful-configurable-cart-item.spec.ts b/libs/cart/state/testing/src/factories/stateful-configurable-cart-item.spec.ts index 87ec8a8b98..410738c837 100644 --- a/libs/cart/state/testing/src/factories/stateful-configurable-cart-item.spec.ts +++ b/libs/cart/state/testing/src/factories/stateful-configurable-cart-item.spec.ts @@ -1,6 +1,7 @@ import { TestBed } from '@angular/core/testing'; -import { DaffStatefulConfigurableCartItem } from '@daffodil/cart/state'; +import { DaffConfigurableCartItem } from '@daffodil/cart'; +import { DaffOperationEntity } from '@daffodil/core/state'; import { DaffStatefulConfigurableCartItemFactory } from './stateful-configurable-cart-item.factory'; @@ -22,7 +23,7 @@ describe('Cart | State | Testing | Factories | StatefulConfigurableCartItemFacto describe('create', () => { - let result: DaffStatefulConfigurableCartItem; + let result: DaffOperationEntity; beforeEach(() => { result = statefulConfigurableCartItemFactory.create(); diff --git a/libs/cart/state/testing/src/mock-cart-facade.ts b/libs/cart/state/testing/src/mock-cart-facade.ts index 70a07d7b51..c64e99d7bc 100644 --- a/libs/cart/state/testing/src/mock-cart-facade.ts +++ b/libs/cart/state/testing/src/mock-cart-facade.ts @@ -5,23 +5,22 @@ import { BehaviorSubject } from 'rxjs'; import { DaffCart, - DaffCartTotal, DaffCartItem, DaffCartOrderResult, DaffConfigurableCartItemAttribute, DaffCompositeCartItemOption, - DaffCartItemDiscount, } from '@daffodil/cart'; import { DaffCartFacadeInterface, DaffCartErrors, DaffCartOperationType, DaffCartLoading, - DaffCartItemStateEnum, - DaffStatefulCartItem, DaffCartResolveState, } from '@daffodil/cart/state'; -import { DaffStateError } from '@daffodil/core/state'; +import { + DaffOperationEntity, + DaffStateError, +} from '@daffodil/core/state'; @Injectable({ providedIn: 'root' }) export class MockDaffCartFacade implements DaffCartFacadeInterface { @@ -71,31 +70,12 @@ export class MockDaffCartFacade implements DaffCartFacadeInterface { paymentMethodsErrors$: BehaviorSubject = new BehaviorSubject([]); couponErrors$: BehaviorSubject = new BehaviorSubject([]); - id$: BehaviorSubject = new BehaviorSubject(null); - subtotal$: BehaviorSubject = new BehaviorSubject(null); - grandTotal$: BehaviorSubject = new BehaviorSubject(null); - subtotalExcludingTax$: BehaviorSubject = new BehaviorSubject(null); - subtotalIncludingTax$: BehaviorSubject = new BehaviorSubject(null); - subtotalWithDiscountExcludingTax$: BehaviorSubject = new BehaviorSubject(null); - subtotalWithDiscountIncludingTax$: BehaviorSubject = new BehaviorSubject(null); - discountTotals$: BehaviorSubject = new BehaviorSubject([]); - totalTax$: BehaviorSubject = new BehaviorSubject(null); - shippingTotal$: BehaviorSubject = new BehaviorSubject(null); - coupons$: BehaviorSubject = new BehaviorSubject([]); - items$: BehaviorSubject = new BehaviorSubject([]); - itemEntities$: BehaviorSubject = new BehaviorSubject([]); + itemEntities$: BehaviorSubject[]> = new BehaviorSubject([]); totalItems$: BehaviorSubject = new BehaviorSubject(null); hasOutOfStockItems$: BehaviorSubject = new BehaviorSubject(false); - outOfStockItems$: BehaviorSubject = new BehaviorSubject([]); - inStockItems$: BehaviorSubject = new BehaviorSubject([]); - itemDictionary$: BehaviorSubject> = new BehaviorSubject({}); - billingAddress$: BehaviorSubject = new BehaviorSubject(null); - shippingAddress$: BehaviorSubject = new BehaviorSubject(null); - payment$: BehaviorSubject = new BehaviorSubject(null); - totals$: BehaviorSubject = new BehaviorSubject(null); - shippingInformation$: BehaviorSubject = new BehaviorSubject(null); - availableShippingMethods$: BehaviorSubject = new BehaviorSubject([]); - availablePaymentMethods$: BehaviorSubject = new BehaviorSubject([]); + outOfStockItems$: BehaviorSubject[]> = new BehaviorSubject([]); + inStockItems$: BehaviorSubject[]> = new BehaviorSubject([]); + itemDictionary$: BehaviorSubject>> = new BehaviorSubject({}); paymentId$: BehaviorSubject = new BehaviorSubject(null); isCartEmpty$: BehaviorSubject = new BehaviorSubject(true); @@ -118,18 +98,6 @@ export class MockDaffCartFacade implements DaffCartFacadeInterface { orderResultCartId$ = new BehaviorSubject(null); hasOrderResult$ = new BehaviorSubject(false); - getCartItemPrice(itemId: DaffCartItem['id']): BehaviorSubject { - return new BehaviorSubject(0); - } - - getCartItemQuantity(itemId: DaffCartItem['id']): BehaviorSubject { - return new BehaviorSubject(0); - } - - getCartItemRowTotal(itemId: DaffCartItem['id']): BehaviorSubject { - return new BehaviorSubject(0); - } - getConfiguredCartItemAttributes(itemId: DaffCartItem['id']): BehaviorSubject { return new BehaviorSubject([]); } @@ -142,21 +110,5 @@ export class MockDaffCartFacade implements DaffCartFacadeInterface { return new BehaviorSubject(false); } - getCartItemState(itemId: DaffCartItem['id']): BehaviorSubject { - return new BehaviorSubject(DaffCartItemStateEnum.Default); - } - - getCartItemDiscounts(itemId: DaffCartItem['id']): BehaviorSubject { - return new BehaviorSubject([]); - } - - getCartItemTotalDiscount(itemId: DaffCartItem['id']): BehaviorSubject { - return new BehaviorSubject(0); - } - - getCartItemErrors(itemId: DaffCartItem['id']): BehaviorSubject { - return new BehaviorSubject([]); - } - dispatch(action: Action) {}; }