Skip to content

Commit

Permalink
feat: extend state management for price notifications, introduce pric…
Browse files Browse the repository at this point in the history
…e notification type, use as example in component
  • Loading branch information
suschneider committed Nov 17, 2022
1 parent e7952c3 commit 5962e04
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { Observable } from 'rxjs';

import { HttpError } from 'ish-core/models/http-error/http-error.model';

import { ProductNotificationType } from '../models/product-notification/product-notification.model';
import {
getAllProductNotifications,
getProductNotificationsByType,
getProductNotificationsError,
getProductNotificationsLoading,
loadProductNotifications,
Expand All @@ -16,9 +17,9 @@ import {
export class ProductNotificationsFacade {
constructor(private store: Store) {}

productNotifications$() {
this.store.dispatch(loadProductNotifications());
return this.store.pipe(select(getAllProductNotifications));
productNotifications$(type: ProductNotificationType) {
this.store.dispatch(loadProductNotifications({ type }));
return this.store.pipe(select(getProductNotificationsByType(type)));
}

productNotificationsLoading$: Observable<boolean> = this.store.pipe(select(getProductNotificationsLoading));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
import { Injectable } from '@angular/core';

import { ProductNotificationData } from './product-notification.interface';
import { ProductNotification } from './product-notification.model';
import { ProductNotification, ProductNotificationType } from './product-notification.model';

@Injectable({ providedIn: 'root' })
export class ProductNotificationMapper {
static fromData(productNotificationData: ProductNotificationData): ProductNotification {
static fromData(
productNotificationData: ProductNotificationData,
notificationType: ProductNotificationType
): ProductNotification {
if (productNotificationData) {
if (productNotificationData.price) {
return {
id: productNotificationData.sku.concat('_price'),
type: 'price',
sku: productNotificationData.sku,
notificationMailAddress: productNotificationData.notificationMailAddress,
price: productNotificationData.price,
};
} else {
return {
id: productNotificationData.sku.concat('_stock'),
type: 'stock',
sku: productNotificationData.sku,
notificationMailAddress: productNotificationData.notificationMailAddress,
price: productNotificationData.price,
};
}
return {
id: productNotificationData.sku.concat('_').concat(notificationType),
type: notificationType,
sku: productNotificationData.sku,
notificationMailAddress: productNotificationData.notificationMailAddress,
price: productNotificationData.price,
};
} else {
throw new Error(`productNotificationData is required`);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Price } from 'ish-core/models/price/price.model';

export type ProductNotificationType = 'stock' | 'price';
export interface ProductNotification {
id: string;
type: 'price' | 'stock';
type: ProductNotificationType;
sku: string;
notificationMailAddress: string;
price?: Price;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
<div class="productnotifications">
<h1>{{ 'account.notifications.heading' | translate }}</h1>

<ng-container *ngIf="productNotifications$ | async as productNotifications">
<ng-container *ngIf="productNotifications?.length > 0" class="list-body">
<div *ngFor="let productNotification of productNotifications">
{{ productNotification.sku }}
</div>
<h3>{{ 'account.notifications.price.heading' | translate }}</h3>
<ng-container *ngIf="productNotificationsPrice$ | async as productNotificationsPrice">
<ng-container *ngIf="productNotificationsPrice?.length > 0" class="list-body">
<ng-container *ngFor="let productNotification of productNotificationsPrice">
<div>
{{ productNotification.sku }} | {{ productNotification.type }} |
<ish-product-name ishProductContext [sku]="productNotification.sku"></ish-product-name>
</div>
</ng-container>
</ng-container>
</ng-container>

<h3>{{ 'account.notifications.backinstock.heading' | translate }}</h3>
<ng-container *ngIf="productNotificationsInStock$ | async as productNotificationsInStock">
<ng-container *ngIf="productNotificationsInStock?.length > 0" class="list-body">
<ng-container *ngFor="let productNotification of productNotificationsInStock">
<div>
{{ productNotification.sku }} | {{ productNotification.type }} |
<ish-product-name ishProductContext [sku]="productNotification.sku"></ish-product-name>
</div>
</ng-container>
</ng-container>
</ng-container>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ import { ProductNotification } from '../../models/product-notification/product-n
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AccountProductNotificationsPageComponent implements OnInit {
productNotifications$: Observable<ProductNotification[]>;
productNotificationsPrice$: Observable<ProductNotification[]>;
productNotificationsInStock$: Observable<ProductNotification[]>;
productNotificationsLoading$: Observable<boolean>;
productNotificationsError$: Observable<HttpError>;

constructor(private productNotificationsFacade: ProductNotificationsFacade) {}

ngOnInit() {
this.productNotifications$ = this.productNotificationsFacade.productNotifications$();
this.productNotificationsPrice$ = this.productNotificationsFacade.productNotifications$('price');
this.productNotificationsInStock$ = this.productNotificationsFacade.productNotifications$('stock');
this.productNotificationsLoading$ = this.productNotificationsFacade.productNotificationsLoading$;
this.productNotificationsError$ = this.productNotificationsFacade.productNotificationsError$;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,32 @@ import { whenTruthy } from 'ish-core/utils/operators';

import { ProductNotificationData } from '../../models/product-notification/product-notification.interface';
import { ProductNotificationMapper } from '../../models/product-notification/product-notification.mapper';
import { ProductNotification } from '../../models/product-notification/product-notification.model';
import {
ProductNotification,
ProductNotificationType,
} from '../../models/product-notification/product-notification.model';

@Injectable({ providedIn: 'root' })
export class ProductNotificationsService {
constructor(private apiService: ApiService, private store: Store) {}

private currentCustomer$ = this.store.pipe(select(getLoggedInCustomer), whenTruthy(), take(1));

getProductNotifications(): Observable<ProductNotification[]> {
getProductNotifications(notificationType: ProductNotificationType): Observable<ProductNotification[]> {
return this.currentCustomer$.pipe(
switchMap(customer =>
this.apiService
.get(
customer.isBusinessCustomer
? `customers/${customer.customerNo}/users/-/notifications/stock`
: `users/-/notifications/stock`
? `customers/${customer.customerNo}/users/-/notifications/${notificationType}`
: `users/-/notifications/${notificationType}`
)
.pipe(
unpackEnvelope<Link>(),
this.apiService.resolveLinks<ProductNotificationData>(),

map(data => data.map(ProductNotificationMapper.fromData))
map(data =>
data.map(notificationData => ProductNotificationMapper.fromData(notificationData, notificationType))
)
)
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ import { createAction } from '@ngrx/store';

import { httpError, payload } from 'ish-core/utils/ngrx-creators';

import { ProductNotification } from '../../models/product-notification/product-notification.model';
import {
ProductNotification,
ProductNotificationType,
} from '../../models/product-notification/product-notification.model';

export const loadProductNotifications = createAction('[ProductNotification] Load ProductNotifications');
export const loadProductNotifications = createAction(
'[ProductNotification] Load ProductNotifications',
payload<{ type: ProductNotificationType }>()
);

export const loadProductNotificationsSuccess = createAction(
'[ProductNotification] Load ProductNotifications Success',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { map, switchMap } from 'rxjs';
import { concatMap, map } from 'rxjs';

import { mapErrorToAction } from 'ish-core/utils/operators';
import { mapErrorToAction, mapToPayload } from 'ish-core/utils/operators';

import { ProductNotificationsService } from '../../services/product-notifications/product-notifications.service';

Expand All @@ -19,8 +19,9 @@ export class ProductNotificationEffects {
loadProductNotifications$ = createEffect(() =>
this.actions$.pipe(
ofType(loadProductNotifications),
switchMap(() =>
this.productNotificationsService.getProductNotifications().pipe(
mapToPayload(),
concatMap(({ type }) =>
this.productNotificationsService.getProductNotifications(type).pipe(
map(productNotifications => loadProductNotificationsSuccess({ productNotifications })),
mapErrorToAction(loadProductNotificationsFail)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { createSelector } from '@ngrx/store';

import {
ProductNotification,
ProductNotificationType,
} from '../../models/product-notification/product-notification.model';
import { getProductNotificationsState } from '../product-notifications-store';

import { initialState, productNotificationAdapter } from './product-notification.reducer';
Expand All @@ -15,3 +19,8 @@ export const getProductNotificationsError = createSelector(getProductNotificatio
const { selectAll } = productNotificationAdapter.getSelectors(getProductNotificationState);

export const getAllProductNotifications = selectAll;

export const getProductNotificationsByType = (type: ProductNotificationType) =>
createSelector(getAllProductNotifications, (entities): ProductNotification[] =>
entities.filter(e => e.type === type)
);
2 changes: 2 additions & 0 deletions src/assets/i18n/de_DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,11 @@
"account.navigation.logout.link": "Abmelden",
"account.navigation.quotes.link": "Preisangebote",
"account.new_user.heading": "Neue Benutzer",
"account.notifications.backinstock.heading": "Wieder lieferbar",
"account.notifications.breadcrumb_link": "Benachrichtigungen",
"account.notifications.heading": "Produktbenachrichtigungen",
"account.notifications.link": "Benachrichtigungen",
"account.notifications.price.heading": "Preisänderungen",
"account.option.select.text": "Bitte auswählen",
"account.order.most_recent.heading": "Letzte Bestellungen",
"account.order.questions.note": "Besuchen Sie die <a href=\"{{0}}\">Hilfe</a> auf unserer Website für umfassende Bestell- und Versandinformationen oder <a href=\"{{1}}\">kontaktieren Sie uns</a> rund um die Uhr.",
Expand Down
2 changes: 2 additions & 0 deletions src/assets/i18n/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,11 @@
"account.navigation.logout.link": "Logout",
"account.navigation.quotes.link": "Quoting",
"account.new_user.heading": "New Users",
"account.notifications.backinstock.heading": "Back in Stock",
"account.notifications.breadcrumb_link": "Notifications",
"account.notifications.heading": "Product Notifications",
"account.notifications.link": "Notifications",
"account.notifications.price.heading": "Changes in Price",
"account.option.select.text": "Please select",
"account.order.most_recent.heading": "Most Recent Orders",
"account.order.questions.note": "Please visit the <a href=\"{{0}}\">Help</a> area of our website for comprehensive order and shipping information or <a href=\"{{1}}\">Contact Us</a> 24 hours a day.",
Expand Down
2 changes: 2 additions & 0 deletions src/assets/i18n/fr_FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,11 @@
"account.navigation.logout.link": "Déconnexion",
"account.navigation.quotes.link": "Devis",
"account.new_user.heading": "Nouveaux utilisateurs",
"account.notifications.backinstock.heading": "Retour en stock",
"account.notifications.breadcrumb_link": "Notifications",
"account.notifications.heading": "Notifications de produits",
"account.notifications.link": "Notifications",
"account.notifications.price.heading": "Changements de prix",
"account.option.select.text": "Veuillez sélectionner",
"account.order.most_recent.heading": "Les commandes les plus récentes",
"account.order.questions.note": "Merci de consulter la section <a href=\"{{0}}\">Aide</a> de notre site Web pour des renseignements détaillés sur votre commande et l’expédition ou <a href=\"{{1}}\">Contactez-nous</a> 24 heures sur 24.",
Expand Down

0 comments on commit 5962e04

Please sign in to comment.