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 }}
+
+
+
+
+
+
+
+
+
+ {{ 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 @@
-