diff --git a/feature-libs/asm/customer-360/assets/translations/en/customer-360.ts b/feature-libs/asm/customer-360/assets/translations/en/customer-360.ts index d7e7aec7bcf..7fa4fdacb5c 100644 --- a/feature-libs/asm/customer-360/assets/translations/en/customer-360.ts +++ b/feature-libs/asm/customer-360/assets/translations/en/customer-360.ts @@ -9,6 +9,8 @@ export const customer360 = { alertErrorMessage: 'The information cannot be be loaded. Please try again later or contact your system administrator.', errorMessageHeader: 'Oops! Something didn\x27t', + applyActionAlter: + "The action couldn't be completed. Please try again later.", header: { title: 'Customer Profile', subTitle: '{{name}} Customer 360\xB0 View', @@ -88,6 +90,13 @@ export const customer360 = { header: 'Support Tickets', emptyDescription: 'There are currently no support tickets', }, + coupons: { + headerText: 'Coupons', + emptyDescription: 'There are currently no coupons', + applyButtonText: 'Apply to Cart', + applied: 'Coupon Applied', + removeButtonText: 'Remove', + }, maps: { storeClosed: 'Close', storesFound: '{{ initial }} - {{ end }} from {{ total }} stores found', @@ -96,7 +105,7 @@ export const customer360 = { profileTab: 'Profile', activityTab: 'Activity', feedbackTab: 'Feedback', - promotionsTab: 'Promotion', + promotionsTab: 'Promotions', mapsTab: 'Maps', aria: { activeCartCode: 'Active Cart {{code}}', diff --git a/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.component.html b/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.component.html new file mode 100644 index 00000000000..a7257a15f53 --- /dev/null +++ b/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.component.html @@ -0,0 +1,83 @@ +
+

+ {{ headerText }} +

+
+
+ + +
+ + + + + {{ + entry.code + }} + + + {{ + entry.name + }} + + + + + + + + + + + + +
+
+ + + + +
+ + + {{ applied }} + + | + + +
+ +
+ {{ emptyStateText }} +
diff --git a/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.component.spec.ts b/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.component.spec.ts new file mode 100644 index 00000000000..d188286b800 --- /dev/null +++ b/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.component.spec.ts @@ -0,0 +1,143 @@ +import { Component, DebugElement, Input } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { AsmCustomerPromotionListingComponent } from './asm-customer-promotion-listing.component'; +import { I18nTestingModule } from '@spartacus/core'; +import { By } from '@angular/platform-browser'; +import { PromotionListEntry } from './asm-customer-promotion-listing.model'; + +describe('AsmCustomerPromotionListingComponent', () => { + const mockEntries: Array = [ + { + code: 'COUPON_1', + name: 'NAME OF COUPON_1', + applied: true, + }, + { + code: 'COUPON_2', + name: 'NAME OF COUPON_2', + applied: false, + }, + { + code: 'COUPON_3', + name: 'NAME OF COUPON_3', + applied: false, + }, + ]; + + const mockEmptyText = 'empty list'; + const mockHeaderText = 'Header Text'; + + @Component({ + selector: 'cx-test-host', + template: ` + + + `, + }) + class TestHostComponent { + @Input() headerText: string; + @Input() emptyStateText: string; + @Input() applyButtonText: string; + @Input() applied: string; + @Input() removeButtonText: string; + @Input() entries: Array | null; + @Input() showAlert: boolean | null; + @Input() showAlertForApplyAction: boolean | null; + @Input() showRemoveButton: boolean; + @Input() showApplyButton: boolean; + apply = void {}; + remove = void {}; + removeAlert = void {}; + removeAlertForApplyAction = void {}; + } + + let component: AsmCustomerPromotionListingComponent; + let fixture: ComponentFixture; + let testHost: TestHostComponent; + let el: DebugElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [I18nTestingModule], + declarations: [TestHostComponent, AsmCustomerPromotionListingComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TestHostComponent); + testHost = fixture.componentInstance; + component = fixture.debugElement.query( + By.directive(AsmCustomerPromotionListingComponent) + ).componentInstance; + el = fixture.debugElement; + }); + + it('should create', () => { + fixture.detectChanges(); + expect(component).toBeTruthy(); + }); + + it('should display header text', () => { + testHost.headerText = mockHeaderText; + fixture.detectChanges(); + + const header = el.query( + By.css('.cx-asm-customer-promotion-listing-heading-text') + ); + expect(header.nativeElement.innerText).toBe(mockHeaderText); + }); + + it('should display entries list', () => { + testHost.entries = mockEntries; + fixture.detectChanges(); + + const entriesList = el.query(By.css('.cx-asm-customer-promotion-listing')); + expect(entriesList).toBeTruthy(); + + const listTableBody = el.query(By.css('table')); + + const rows = listTableBody.queryAll( + By.css('.cx-asm-customer-promotion-listing-row') + ); + expect(rows.length).toBe(mockEntries.length); + }); + + it('should display empty message when entries is empty', () => { + testHost.emptyStateText = mockEmptyText; + testHost.entries = []; + fixture.detectChanges(); + + const emptyMessage = el.query( + By.css('.cx-asm-customer-promotion-listing-empty') + ); + + expect(emptyMessage).toBeTruthy(); + }); + + it('should show meaasge when entries loaded failed', () => { + testHost.showAlert = true; + fixture.detectChanges(); + expect(el.query(By.css('cx-message'))).not.toBeNull(); + }); + + it('should show meaasge when action perform failed', () => { + testHost.showAlertForApplyAction = true; + fixture.detectChanges(); + expect(el.query(By.css('cx-message'))).not.toBeNull(); + }); +}); diff --git a/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.component.ts b/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.component.ts new file mode 100644 index 00000000000..5e87d378af2 --- /dev/null +++ b/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.component.ts @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, +} from '@angular/core'; +import { GlobalMessageType } from '@spartacus/core'; +import { PromotionListEntry } from './asm-customer-promotion-listing.model'; +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'cx-asm-customer-promotion-listing', + templateUrl: './asm-customer-promotion-listing.component.html', +}) +export class AsmCustomerPromotionListingComponent { + @Input() headerText: string; + @Input() emptyStateText: string; + @Input() applyButtonText: string; + @Input() applied: string; + @Input() removeButtonText: string; + @Input() entries: Array | null; + @Input() showAlert: boolean | null; + @Input() showAlertForApplyAction: boolean | null; + @Input() showRemoveButton: boolean; + @Input() showApplyButton: boolean; + @Output() apply = new EventEmitter(); + @Output() remove = new EventEmitter(); + @Output() removeAlert = new EventEmitter(); + @Output() removeAlertForApplyAction = new EventEmitter(); + globalMessageType = GlobalMessageType; +} diff --git a/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.model.ts b/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.model.ts new file mode 100644 index 00000000000..132b9041b6e --- /dev/null +++ b/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.model.ts @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export interface PromotionListEntry { + [key: string]: string | boolean | undefined; +} + +export interface GeneralEntry extends PromotionListEntry { + applied: boolean; + code: string; + name?: string; +} diff --git a/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.module.ts b/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.module.ts new file mode 100644 index 00000000000..7b671169d8f --- /dev/null +++ b/feature-libs/asm/customer-360/components/asm-customer-promotion-listing/asm-customer-promotion-listing.module.ts @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { ArgsModule } from '@spartacus/asm/core'; +import { I18nModule } from '@spartacus/core'; +import { + StarRatingModule, + IconModule, + MessageComponentModule, +} from '@spartacus/storefront'; +import { AsmCustomerPromotionListingComponent } from './asm-customer-promotion-listing.component'; + +@NgModule({ + declarations: [AsmCustomerPromotionListingComponent], + exports: [AsmCustomerPromotionListingComponent], + imports: [ + CommonModule, + I18nModule, + ArgsModule, + StarRatingModule, + MessageComponentModule, + IconModule, + ], +}) +export class AsmCustomerPromotionListingModule {} diff --git a/feature-libs/asm/customer-360/components/customer-360-components.module.ts b/feature-libs/asm/customer-360/components/customer-360-components.module.ts index ad8693cccb3..70956e99ea2 100644 --- a/feature-libs/asm/customer-360/components/customer-360-components.module.ts +++ b/feature-libs/asm/customer-360/components/customer-360-components.module.ts @@ -7,7 +7,11 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ArgsModule } from '@spartacus/asm/core'; -import { I18nModule, provideDefaultConfig } from '@spartacus/core'; +import { + FeaturesConfigModule, + I18nModule, + provideDefaultConfig, +} from '@spartacus/core'; import { IconModule, KeyboardFocusModule, @@ -36,6 +40,8 @@ import { AsmCustomerProfileComponent, AsmCustomerSavedCartComponent, } from './sections/components'; +import { AsmCustomerCouponComponent } from './sections/asm-customer-coupon/asm-customer-coupon.component'; +import { AsmCustomerCouponComponentModule } from './sections/asm-customer-coupon/asm-customer-coupon.module'; @NgModule({ imports: [ @@ -56,6 +62,8 @@ import { AsmCustomerMapComponentModule, AsmCustomerProductReviewsComponentModule, AsmCustomerSupportTicketsComponentModule, + AsmCustomerCouponComponentModule, + FeaturesConfigModule, ], declarations: [Customer360Component, AsmCustomerSectionComponent], exports: [Customer360Component], @@ -89,6 +97,9 @@ import { AsmCustomer360MapComponent: { component: AsmCustomerMapComponent, }, + AsmCustomer360CouponComponent: { + component: AsmCustomerCouponComponent, + }, }, }), ], diff --git a/feature-libs/asm/customer-360/components/customer-360/customer-360.component.html b/feature-libs/asm/customer-360/components/customer-360/customer-360.component.html index e32d74c07de..84b6c84a2c7 100644 --- a/feature-libs/asm/customer-360/components/customer-360/customer-360.component.html +++ b/feature-libs/asm/customer-360/components/customer-360/customer-360.component.html @@ -162,7 +162,24 @@