Skip to content

Commit

Permalink
feat: remove quote items from cart (#1029)
Browse files Browse the repository at this point in the history
Co-authored-by: Luis Garcia <luis.miguel.garcia@evident.nl>
Co-authored-by: Danilo Hoffmann <dhhyi@aol.com>
  • Loading branch information
3 people authored and SGrueber committed Apr 1, 2022
1 parent 0cef1f1 commit af26336
Show file tree
Hide file tree
Showing 15 changed files with 215 additions and 13 deletions.
1 change: 1 addition & 0 deletions src/app/core/models/line-item/line-item.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ export interface LineItemData {
hiddenGift: boolean;
freeGift: boolean;
quantityFixed?: boolean;
quote?: string;
}
1 change: 1 addition & 0 deletions src/app/core/models/line-item/line-item.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export class LineItemMapper {

productSKU: data.product,
editable: !data.quantityFixed,
quote: data.quote ? data.quote : undefined,
};
} else {
throw new Error(`'LineItemData' is required for the mapping`);
Expand Down
1 change: 1 addition & 0 deletions src/app/core/models/line-item/line-item.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface LineItem {
isFreeGift: boolean;

editable: boolean;
quote?: string;
}

export interface LineItemView extends LineItem {
Expand Down
3 changes: 3 additions & 0 deletions src/app/extensions/quoting/exports/quoting-exports.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { LazyBasketAddToQuoteComponent } from './lazy-basket-add-to-quote/lazy-b
import { LazyProductAddToQuoteComponent } from './lazy-product-add-to-quote/lazy-product-add-to-quote.component';
import { LazyQuoteStateComponent } from './lazy-quote-state/lazy-quote-state.component';
import { LazyQuoteWidgetComponent } from './lazy-quote-widget/lazy-quote-widget.component';
import { LazyQuotingBasketLineItemsComponent } from './lazy-quoting-basket-line-items/lazy-quoting-basket-line-items.component';

@NgModule({
imports: [FeatureToggleModule],
Expand All @@ -25,12 +26,14 @@ import { LazyQuoteWidgetComponent } from './lazy-quote-widget/lazy-quote-widget.
LazyProductAddToQuoteComponent,
LazyQuoteStateComponent,
LazyQuoteWidgetComponent,
LazyQuotingBasketLineItemsComponent,
],
exports: [
LazyBasketAddToQuoteComponent,
LazyProductAddToQuoteComponent,
LazyQuoteStateComponent,
LazyQuoteWidgetComponent,
LazyQuotingBasketLineItemsComponent,
],
})
export class QuotingExportsModule {}
22 changes: 19 additions & 3 deletions src/app/extensions/quoting/facades/quoting.facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { map, mapTo, sample, switchMap, switchMapTo, tap } from 'rxjs/operators'
import { whenFalsy } from 'ish-core/utils/operators';

import { QuotingHelper } from '../models/quoting/quoting.helper';
import { QuotingEntity } from '../models/quoting/quoting.model';
import { Quote, QuoteRequest, QuotingEntity } from '../models/quoting/quoting.model';
import {
createQuoteRequestFromBasket,
deleteQuoteFromBasket,
deleteQuotingEntity,
getQuotingEntities,
getQuotingEntity,
Expand All @@ -26,7 +27,7 @@ export class QuotingFacade {

quotingEntities$() {
// update on subscription
this.store.dispatch(loadQuoting());
this.loadQuoting();

return this.store.pipe(
select(getQuotingEntities),
Expand All @@ -36,7 +37,7 @@ export class QuotingFacade {
timer(0, 60_000).pipe(
tap(count => {
if (count) {
this.store.dispatch(loadQuoting());
this.loadQuoting();
}
}),
mapTo(entities)
Expand All @@ -58,11 +59,26 @@ export class QuotingFacade {
);
}

name$(quoteId: string) {
return this.store.pipe(
select(getQuotingEntity(quoteId)),
map((quote: Quote | QuoteRequest) => quote?.displayName)
);
}

delete(entity: QuotingEntity) {
this.store.dispatch(deleteQuotingEntity({ entity }));
}

createQuoteRequestFromBasket() {
this.store.dispatch(createQuoteRequestFromBasket());
}

loadQuoting() {
this.store.dispatch(loadQuoting());
}

deleteQuoteFromBasket(quoteId: string) {
this.store.dispatch(deleteQuoteFromBasket({ id: quoteId }));
}
}
3 changes: 3 additions & 0 deletions src/app/extensions/quoting/quoting.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { QuoteLineItemListComponent } from './shared/quote-line-item-list/quote-
import { QuoteStateComponent } from './shared/quote-state/quote-state.component';
import { QuoteViewComponent } from './shared/quote-view/quote-view.component';
import { QuoteWidgetComponent } from './shared/quote-widget/quote-widget.component';
import { QuotingBasketLineItemsComponent } from './shared/quoting-basket-line-items/quoting-basket-line-items.component';

@NgModule({
imports: [SharedModule],
Expand All @@ -28,6 +29,7 @@ import { QuoteWidgetComponent } from './shared/quote-widget/quote-widget.compone
QuoteStateComponent,
QuoteViewComponent,
QuoteWidgetComponent,
QuotingBasketLineItemsComponent,
],
exports: [
ProductAddToQuoteComponent,
Expand All @@ -37,6 +39,7 @@ import { QuoteWidgetComponent } from './shared/quote-widget/quote-widget.compone
QuoteStateComponent,
QuoteViewComponent,
QuoteWidgetComponent,
QuotingBasketLineItemsComponent,
SharedModule,
],
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<ng-container *ngFor="let item of lineItems$ | async">
<div class="row" *ngIf="item[0] !== 'undefined'">
<div class="col-12 col-sm-10 my-auto">
<h2>{{ 'quote.basket_items.label' | translate: { quoteId: getName(item[0]) | async } }}</h2>
</div>
<div class="col-12 col-sm-2 my-auto text-right">
<a
class="btn-tool"
title="{{ 'shopping_cart.remove.item.button.label' | translate }}"
(click)="onDeleteQuote(item[0])"
>
<fa-icon [icon]="['fas', 'trash-alt']"></fa-icon>
</a>
</div>
</div>
<ish-line-item-list [lineItems]="item[1]"></ish-line-item-list>
</ng-container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EMPTY } from 'rxjs';
import { instance, mock, when } from 'ts-mockito';

import { CheckoutFacade } from 'ish-core/facades/checkout.facade';

import { QuotingFacade } from '../../facades/quoting.facade';

import { QuotingBasketLineItemsComponent } from './quoting-basket-line-items.component';

describe('QuotingBasketLineItemsComponent', () => {
let component: QuotingBasketLineItemsComponent;
let fixture: ComponentFixture<QuotingBasketLineItemsComponent>;
let element: HTMLElement;

let checkoutFacade: CheckoutFacade;
let quotingFacade: QuotingFacade;

beforeEach(async () => {
checkoutFacade = mock(CheckoutFacade);
quotingFacade = mock(QuotingFacade);
when(checkoutFacade.basket$).thenReturn(EMPTY);
await TestBed.configureTestingModule({
declarations: [QuotingBasketLineItemsComponent],
providers: [
{ provide: CheckoutFacade, useFactory: () => instance(checkoutFacade) },
{ provide: QuotingFacade, useFactory: () => instance(quotingFacade) },
],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(QuotingBasketLineItemsComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
});

it('should be created', () => {
expect(component).toBeTruthy();
expect(element).toBeTruthy();
expect(() => fixture.detectChanges()).not.toThrow();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { groupBy } from 'lodash-es';
import { Observable, map } from 'rxjs';

import { CheckoutFacade } from 'ish-core/facades/checkout.facade';
import { LineItem } from 'ish-core/models/line-item/line-item.model';
import { GenerateLazyComponent } from 'ish-core/utils/module-loader/generate-lazy-component.decorator';

import { QuotingFacade } from '../../facades/quoting.facade';

@Component({
selector: 'ish-quoting-basket-line-items',
templateUrl: './quoting-basket-line-items.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
@GenerateLazyComponent()
export class QuotingBasketLineItemsComponent implements OnInit {
lineItems$: Observable<[string, LineItem[]][]>;

constructor(private checkoutFacade: CheckoutFacade, private quotingFacade: QuotingFacade) {}

ngOnInit() {
this.quotingFacade.loadQuoting();
this.lineItems$ = this.checkoutFacade.basket$.pipe(
map(basket => Object.entries(groupBy(basket.lineItems, 'quote')).sort((a, _) => (a[0] === 'undefined' ? -1 : 0)))
);
}

getName(quoteId: string) {
return this.quotingFacade.name$(quoteId);
}

onDeleteQuote(quoteId: string) {
this.quotingFacade.deleteQuoteFromBasket(quoteId);
}
}
2 changes: 2 additions & 0 deletions src/app/extensions/quoting/store/quoting/quoting.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export const deleteQuotingEntitySuccess = createAction(
payload<IdPayloadType>()
);

export const deleteQuoteFromBasket = createAction('[Quoting] Delete Quote From Basket', payload<IdPayloadType>());

export const rejectQuote = createAction('[Quoting] Reject Quote', payload<IdPayloadType>());

export const rejectQuoteFail = createAction('[Quoting API] Reject Quote Fail', httpError<IdPayloadType>());
Expand Down
54 changes: 52 additions & 2 deletions src/app/extensions/quoting/store/quoting/quoting.effects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import { Observable, noop, of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { anything, capture, instance, mock, spy, verify, when } from 'ts-mockito';

import { Basket } from 'ish-core/models/basket/basket.model';
import { Basket, BasketView } from 'ish-core/models/basket/basket.model';
import { BasketService } from 'ish-core/services/basket/basket.service';
import { getCurrentBasketId } from 'ish-core/store/customer/basket';
import { getCurrentBasket, getCurrentBasketId } from 'ish-core/store/customer/basket';

import { QuoteRequest, QuoteStub } from '../../models/quoting/quoting.model';
import { QuotingService } from '../../services/quoting/quoting.service';
Expand All @@ -24,6 +24,7 @@ import {
createQuoteRequestFromQuote,
createQuoteRequestFromQuoteRequest,
createQuoteRequestFromQuoteSuccess,
deleteQuoteFromBasket,
deleteQuotingEntity,
loadQuoting,
loadQuotingDetail,
Expand Down Expand Up @@ -381,4 +382,53 @@ describe('Quoting Effects', () => {
});
});
});

describe('deleteQuoteFromBasket$', () => {
it('should dispatch deleteBasketItem action for quote related item', done => {
actions$ = of(deleteQuoteFromBasket({ id: 'QUOTE' }));

store$.overrideSelector(getCurrentBasket, {
lineItems: [
{ id: '1' },
{ id: '2', quote: 'QUOTE' },
{ id: '3', quote: 'QUOTE' },
{ id: '4', quote: 'QUOTE2' },
],
} as BasketView);

effects.deleteQuoteFromBasket$.subscribe(action => {
expect(action).toMatchInlineSnapshot(`
[Basket] Delete Basket Item:
itemId: "2"
`);
done();
});
});

it('should do nothing when quote is not in the basket', done => {
actions$ = of(deleteQuoteFromBasket({ id: 'QUOTE' }));

store$.overrideSelector(getCurrentBasket, {
lineItems: [{ id: '1' }],
} as BasketView);

effects.deleteQuoteFromBasket$.subscribe({
next: fail,
error: fail,
complete: done,
});
});

it('should do nothing when there is no basket', done => {
actions$ = of(deleteQuoteFromBasket({ id: 'QUOTE' }));

store$.overrideSelector(getCurrentBasket, undefined);

effects.deleteQuoteFromBasket$.subscribe({
next: fail,
error: fail,
complete: done,
});
});
});
});
24 changes: 21 additions & 3 deletions src/app/extensions/quoting/store/quoting/quoting.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ import { BasketService } from 'ish-core/services/basket/basket.service';
import { displaySuccessMessage } from 'ish-core/store/core/messages';
import { selectRouteParam, selectUrl } from 'ish-core/store/core/router';
import { setBreadcrumbData } from 'ish-core/store/core/viewconf';
import { getCurrentBasketId, updateBasket } from 'ish-core/store/customer/basket';
import { mapErrorToAction, mapToPayload, mapToPayloadProperty, mapToProperty } from 'ish-core/utils/operators';
import { deleteBasketItem, getCurrentBasket, getCurrentBasketId, updateBasket } from 'ish-core/store/customer/basket';
import {
mapErrorToAction,
mapToPayload,
mapToPayloadProperty,
mapToProperty,
whenTruthy,
} from 'ish-core/utils/operators';

import { QuotingHelper } from '../../models/quoting/quoting.helper';
import { QuotingService } from '../../services/quoting/quoting.service';
Expand Down Expand Up @@ -40,6 +46,7 @@ import {
submitQuoteRequestSuccess,
updateQuoteRequest,
updateQuoteRequestSuccess,
deleteQuoteFromBasket,
} from './quoting.actions';
import { getQuotingEntity } from './quoting.selectors';

Expand All @@ -56,7 +63,7 @@ export class QuotingEffects {
loadQuoting$ = createEffect(() =>
this.actions$.pipe(
ofType(loadQuoting),
switchMap(() =>
mergeMap(() =>
this.quotingService.getQuotes().pipe(
map(quoting => loadQuotingSuccess({ quoting })),
mapErrorToAction(loadQuotingFail)
Expand Down Expand Up @@ -111,6 +118,17 @@ export class QuotingEffects {
)
);

deleteQuoteFromBasket$ = createEffect(() =>
this.actions$.pipe(
ofType(deleteQuoteFromBasket),
mapToPayloadProperty('id'),
withLatestFrom(this.store.pipe(select(getCurrentBasket))),
map(([quoteId, basket]) => basket?.lineItems?.find(li => li.quote === quoteId)?.id),
whenTruthy(),
map(itemId => deleteBasketItem({ itemId }))
)
);

addQuoteToBasket$ = createEffect(() =>
this.actions$.pipe(
ofType(addQuoteToBasket),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@ <h1 class="d-flex flex-wrap align-items-baseline">
</div>

<ish-basket-cost-center-selection></ish-basket-cost-center-selection>

<div class="section">
<ish-line-item-list *ngIf="basket" [lineItems]="basket.lineItems"></ish-line-item-list>
<ng-container *ishFeature="'quoting'; else elseTemplate">
<ish-lazy-quoting-basket-line-items></ish-lazy-quoting-basket-line-items>
</ng-container>
<ng-template #elseTemplate>
<ish-line-item-list *ngIf="basket" [lineItems]="basket.lineItems"></ish-line-item-list>
</ng-template>

<div class="button-group">
<div class="button-group" *ngIf="!basketContainsQuote">
<ish-lazy-basket-add-to-quote></ish-lazy-basket-add-to-quote>
<ish-lazy-basket-create-order-template
*ngIf="basket"
Expand Down
Loading

0 comments on commit af26336

Please sign in to comment.