Добавил состояние приложения(профиль)
Сделал проверку на активность карты, на то, хватает ли баланса для совершения заказа, доработал обработку ошибок, пофиксил некоторые баги
This commit is contained in:
gofnnp 2023-05-26 01:35:37 +04:00
parent ab8e909b08
commit 2428870ba8
16 changed files with 601 additions and 168 deletions

View File

@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { PrimeNGConfig } from 'primeng/api'; import { PrimeNGConfig } from 'primeng/api';
import * as ConfigActions from './state/config/config.actions'; import * as ConfigActions from './state/config/config.actions';
import * as ProfileActions from './state/profile/profile.actions';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -16,5 +17,6 @@ export class AppComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.primengConfig.ripple = false; this.primengConfig.ripple = false;
this.store.dispatch(ConfigActions.getConfig()); this.store.dispatch(ConfigActions.getConfig());
this.store.dispatch(ProfileActions.getProfile());
} }
} }

View File

@ -77,6 +77,9 @@ import { PurchaseInfoComponent } from './components/purchase-info/purchase-info.
import { DateFilterComponent } from './components/date-filter/date-filter.component'; import { DateFilterComponent } from './components/date-filter/date-filter.component';
import {MatDatepickerModule} from '@angular/material/datepicker'; import {MatDatepickerModule} from '@angular/material/datepicker';
import {MatNativeDateModule} from '@angular/material/core'; import {MatNativeDateModule} from '@angular/material/core';
import { profileReducer } from './state/profile/profile.reducer';
import { ProfileEffects } from './state/profile/profile.effects';
import { CustomerInfoInterceptor } from './interceptors/customer-info.interceptor';
const routes: Routes = [ const routes: Routes = [
{ path: '', redirectTo: 'products', pathMatch: 'full' }, { path: '', redirectTo: 'products', pathMatch: 'full' },
@ -152,8 +155,8 @@ const routes: Routes = [
CalendarModule, CalendarModule,
MatIconModule, MatIconModule,
MdbCarouselModule, MdbCarouselModule,
StoreModule.forRoot({ config: configReducer }), StoreModule.forRoot({ config: configReducer, profile: profileReducer }),
EffectsModule.forRoot([ConfigEffects]), EffectsModule.forRoot([ConfigEffects, ProfileEffects]),
PaginatorModule, PaginatorModule,
InputTextModule, InputTextModule,
SidebarModule, SidebarModule,
@ -178,6 +181,11 @@ const routes: Routes = [
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
useValue: { appearance: 'outline' }, useValue: { appearance: 'outline' },
}, },
{
provide: HTTP_INTERCEPTORS,
useClass: CustomerInfoInterceptor,
multi: true,
},
], ],
bootstrap: [AppComponent], bootstrap: [AppComponent],
}) })

View File

@ -0,0 +1,50 @@
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpResponse,
HttpErrorResponse,
} from '@angular/common/http';
import { Observable, tap } from 'rxjs';
import { MatSnackBar } from '@angular/material/snack-bar';
@Injectable()
export class CustomerInfoInterceptor implements HttpInterceptor {
constructor(private _snackBar: MatSnackBar) {}
intercept(
request: HttpRequest<unknown>,
next: HttpHandler
): Observable<HttpEvent<unknown>> {
return next.handle(request).pipe(
tap(
(event) => {
if (event instanceof HttpResponse) {
const body = event.body as any;
const url = new URL(event.url || '');
if (url.pathname.includes('/icard-proxy/customer_info')) {
if (
body.customer_info?.errorCode === 'Customer_CustomerNotFound'
) {
this._snackBar.open(
'Пользователь не найден в системе! Обратитесь к руководству',
'Ок'
);
} else if ('Error' in body) {
this._snackBar.open('Произошла ошибка! Попробуйте снова', 'Ок');
}
}
}
if (event instanceof HttpErrorResponse) {
this._snackBar.open('Произошла ошибка! Попробуйте снова', 'Ок');
}
},
(err) => {
console.error('ERROR FROM INTERCEPTOR: ', err);
}
)
);
}
}

View File

@ -35,7 +35,7 @@ export interface UserDataForm {
export interface BonusProgramAccount { export interface BonusProgramAccount {
// BonusProgramName: string; // BonusProgramName: string;
// BonusProgramTypeID: string; // BonusProgramTypeID: string;
CardNumber: number; CardNumber: string;
Bonuses: number; Bonuses: number;
// HoldedBonuses: number; // HoldedBonuses: number;
// BonusProgramAccounts: BonusProgramAccount[]; // BonusProgramAccounts: BonusProgramAccount[];
@ -286,3 +286,66 @@ export interface IDateFilter {
from?: Date; from?: Date;
to?: Date; to?: Date;
} }
export interface ICustomerInfo {
anonymized: boolean;
birthday: string | Date;
cards: ICardCustomer[];
categories: ICategoriesCustomer[];
comment: string | null;
consentStatus: number;
cultureName: string;
email: string | null;
id: string;
iikoCardOrdersSum: number;
isBlocked: boolean;
isDeleted: boolean;
middleName: string | null;
name: string;
personalDataConsentFrom: string | null;
personalDataConsentTo: string | null;
personalDataProcessingFrom: string | null;
personalDataProcessingTo: string | null;
phone: string;
rank: string | null;
referrerId: string | null;
sex: number;
shouldReceiveLoyaltyInfo: boolean;
shouldReceiveOrderStatusInfo: boolean;
shouldReceivePromoActionsInfo: boolean;
surname: string;
userData: any;
walletBalances: IWalletBalance[];
errorCode?: string;
first_name: string | null;
last_name: string | null;
street: string | null;
house: string | null;
flat: string | null;
city: string;
selectedTerminal: ITerminal | null;
}
export interface ICardCustomer {
Id: string;
IsActivated: boolean;
NetworkId: string | null;
Number: string;
OrganizationId: string;
OrganizationName: string | null;
Track: string;
ValidToDate: string | Date;
}
export interface ICategoriesCustomer {}
export interface IWalletBalance {
balance: number;
wallet: {
id: string;
name: string;
programType: string;
type: string;
};
}

View File

@ -11,6 +11,8 @@ import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { lastValueFrom } from 'rxjs'; import { lastValueFrom } from 'rxjs';
import { CartService } from 'src/app/services/cart.service'; import { CartService } from 'src/app/services/cart.service';
import { Store } from '@ngrx/store';
import { getProfile, getTransactions, getTransactionsInfo } from 'src/app/state/profile/profile.actions';
@Component({ @Component({
selector: 'app-account', selector: 'app-account',
@ -31,6 +33,7 @@ export class AccountComponent implements OnInit {
private jsonRpcService: JsonrpcService, private jsonRpcService: JsonrpcService,
private messageService: MessageService, private messageService: MessageService,
private cartService: CartService, private cartService: CartService,
private store: Store
) { } ) { }
public currentPage!: Page; public currentPage!: Page;
@ -52,6 +55,12 @@ export class AccountComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.checkAuthorization(true) this.checkAuthorization(true)
this.store.dispatch(getProfile())
this.store.dispatch(getTransactions())
this.store.dispatch(getTransactionsInfo())
this.currentPage = this.pageList[0]; this.currentPage = this.pageList[0];
document.body.classList.add( document.body.classList.add(
'woocommerce-account', 'woocommerce-account',

View File

@ -20,10 +20,10 @@
</div> </div>
<div *ngIf="accountData" class="info__row"> <div *ngIf="accountData" class="info__row">
<span class="key">Имя</span> <span class="key">Имя</span>
<span class="value" *ngIf="userName.length">{{ <span class="value" *ngIf="userName?.length">{{
userName userName
}}</span> }}</span>
<span class="value" *ngIf="!userName.length" <span class="value" *ngIf="!userName?.length"
>Не задано</span >Не задано</span
> >
</div> </div>

View File

@ -1,7 +1,23 @@
import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core'; import {
import { lastValueFrom } from 'rxjs'; Component,
import { orderStatuses, PageList, PageListWithBonus } from 'src/app/app.constants'; EventEmitter,
import { BonusProgramAccount, Page, Purchase, Transaction } from 'src/app/interface/data'; Inject,
Input,
OnInit,
Output,
} from '@angular/core';
import { distinctUntilChanged, lastValueFrom } from 'rxjs';
import {
orderStatuses,
PageList,
PageListWithBonus,
} from 'src/app/app.constants';
import {
BonusProgramAccount,
Page,
Purchase,
Transaction,
} from 'src/app/interface/data';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
import * as moment from 'moment-timezone'; import * as moment from 'moment-timezone';
import * as barcode from 'jsbarcode'; import * as barcode from 'jsbarcode';
@ -9,38 +25,36 @@ import { environment } from 'src/environments/environment';
import { AppleWalletService } from 'src/app/services/apple-wallet.service'; import { AppleWalletService } from 'src/app/services/apple-wallet.service';
import { CookiesService } from 'src/app/services/cookies.service'; import { CookiesService } from 'src/app/services/cookies.service';
import { DOCUMENT } from '@angular/common'; import { DOCUMENT } from '@angular/common';
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Store } from '@ngrx/store';
import { WpJsonService } from 'src/app/services/wp-json.service'; import { selectCustomerInfo } from 'src/app/state/profile/profile.reducer';
import { MatSnackBar } from '@angular/material/snack-bar';
@Component({ @Component({
selector: 'app-bonus-program', selector: 'app-bonus-program',
templateUrl: './bonus-program.component.html', templateUrl: './bonus-program.component.html',
styleUrls: ['./bonus-program.component.scss'] styleUrls: ['./bonus-program.component.scss'],
}) })
export class BonusProgramComponent implements OnInit { export class BonusProgramComponent implements OnInit {
@Input() currentPage!: Page; @Input() currentPage!: Page;
@Output() deauthorization = new EventEmitter<boolean>(false) @Output() deauthorization = new EventEmitter<boolean>(false);
public accountData!: BonusProgramAccount; public accountData!: BonusProgramAccount;
public purchases: Purchase[] = []; public purchases: Purchase[] = [];
public loadingBonuses: boolean = false; public loadingBonuses: boolean = false;
readonly orderStatuses = orderStatuses; readonly orderStatuses = orderStatuses;
readonly moment = moment; readonly moment = moment;
readonly pageList = environment.hasBonusProgram ? PageListWithBonus : PageList; readonly pageList = environment.hasBonusProgram
? PageListWithBonus
: PageList;
public userName: string = ''; public userName: string = '';
public deviceType: 'ios' | 'android' | null = null; public deviceType: 'ios' | 'android' | null = null;
private profile$ = this.store.select(selectCustomerInfo);
constructor( constructor(
private jsonrpc: JsonrpcService, private jsonrpc: JsonrpcService,
private appleWallet: AppleWalletService, private appleWallet: AppleWalletService,
private cookiesService: CookiesService, private cookiesService: CookiesService,
@Inject(DOCUMENT) private document: Document, @Inject(DOCUMENT) private document: Document,
private http: HttpClient, private store: Store
private wpJsonService: WpJsonService, ) {}
private _snackBar: MatSnackBar
) { }
ngOnInit(): void { ngOnInit(): void {
this.getAccountData(); this.getAccountData();
@ -48,50 +62,54 @@ export class BonusProgramComponent implements OnInit {
} }
getAccountData() { getAccountData() {
const token = this.cookiesService.getItem('token') const token = this.cookiesService.getItem('token');
if (!token) { if (!token) {
this.cookiesService.deleteCookie('token') this.cookiesService.deleteCookie('token');
this.deauthorization.emit(true) this.deauthorization.emit(true);
return return;
} }
this.loadingBonuses = true; this.loadingBonuses = true;
this.wpJsonService.getCustomerInfo(environment.systemId, token, environment.icardProxy).subscribe({ this.profile$.pipe(distinctUntilChanged()).subscribe({
next: (res) => { next: (res) => {
if (res.customer_info?.errorCode === 'Customer_CustomerNotFound' || 'Error' in res) { if (!res) return;
// this._snackBar.open('Пользователь не найден в системе! Обратитесь к руководству', 'Ок') if (res.errorCode === 'Customer_CustomerNotFound' || 'Error' in res) {
this.loadingBonuses = false; this.loadingBonuses = false;
return return;
} }
this.userName = res.customer_info.name this.userName = res.name;
this.accountData = { this.accountData = {
CardNumber: res.customer_info.cards[0]?.Number || '', CardNumber: res.cards[0]?.Number || '',
Bonuses: res.customer_info.walletBalances[0].balance Bonuses: res.walletBalances[0].balance,
};
if (this.document.getElementById('barcode')) {
barcode('#barcode')
.options({ font: 'OCR-B' }) // Will affect all barcodes
.EAN13(`${this.accountData.CardNumber}`.padStart(12, '0'), {
fontSize: 18,
textMargin: 0,
})
.render();
} }
barcode("#barcode")
.options({ font: "OCR-B" }) // Will affect all barcodes
.EAN13(`${this.accountData.CardNumber}`.padStart(12, "0"), { fontSize: 18, textMargin: 0 })
.render();
this.loadingBonuses = false; this.loadingBonuses = false;
}, },
error: (err) => { error: (err) => {
console.error('Error: ', err) console.error('Error: ', err);
} },
}) });
} }
getTypeDevice() { getTypeDevice() {
const userAgent = window.navigator.userAgent.toLowerCase() const userAgent = window.navigator.userAgent.toLowerCase();
const ios = /iphone|ipod|ipad/.test(userAgent); const ios = /iphone|ipod|ipad/.test(userAgent);
this.deviceType = ios ? 'ios' : 'android' this.deviceType = ios ? 'ios' : 'android';
} }
async addCardToWallet(e: any) { async addCardToWallet(e: any) {
e.preventDefault() e.preventDefault();
const token = this.cookiesService.getItem('token') const token = this.cookiesService.getItem('token');
const accountData = (await lastValueFrom( const accountData = (
this.jsonrpc await lastValueFrom(
.rpc( this.jsonrpc.rpc(
{ {
method: 'getTokenData', method: 'getTokenData',
params: [], params: [],
@ -99,19 +117,17 @@ export class BonusProgramComponent implements OnInit {
RpcService.authService, RpcService.authService,
true true
) )
)).data )
).data;
if (token && accountData.user_id) { if (token && accountData.user_id) {
this.appleWallet.generateCard(token, accountData.user_id).subscribe({ this.appleWallet.generateCard(token, accountData.user_id).subscribe({
next: (res: any) => { next: (res: any) => {
this.document.location.href = res.url this.document.location.href = res.url;
}, },
error: (err) => { error: (err) => {
console.log('Error: ', err); console.log('Error: ', err);
},
} });
})
} }
} }
} }

View File

@ -1,14 +1,36 @@
import { HttpErrorResponse } from '@angular/common/http'; import { HttpErrorResponse } from '@angular/common/http';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { IDateFilter, Page, Purchase, PurchaseInfo } from 'src/app/interface/data'; import {
IDateFilter,
Page,
Purchase,
PurchaseInfo,
} from 'src/app/interface/data';
import * as moment from 'moment-timezone'; import * as moment from 'moment-timezone';
import { lastValueFrom } from 'rxjs'; import {
combineLatest,
combineLatestWith,
distinctUntilChanged,
forkJoin,
lastValueFrom,
} from 'rxjs';
import { WpJsonService } from 'src/app/services/wp-json.service'; import { WpJsonService } from 'src/app/services/wp-json.service';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
import { CookiesService } from 'src/app/services/cookies.service'; import { CookiesService } from 'src/app/services/cookies.service';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { PurchaseInfoComponent } from 'src/app/components/purchase-info/purchase-info.component'; import { PurchaseInfoComponent } from 'src/app/components/purchase-info/purchase-info.component';
import { dateFilterOptions } from 'src/app/app.constants'; import { dateFilterOptions } from 'src/app/app.constants';
import { Store, select } from '@ngrx/store';
import {
selectCustomerInfo,
selectProfileState,
selectTransactions,
selectTransactionsInfo,
} from 'src/app/state/profile/profile.reducer';
import {
getTransactions,
getTransactionsInfo,
} from 'src/app/state/profile/profile.actions';
@Component({ @Component({
selector: 'app-orders', selector: 'app-orders',
@ -25,16 +47,20 @@ export class OrdersComponent implements OnInit {
public purchases: Purchase[] = []; public purchases: Purchase[] = [];
public purchasesShortArray: Purchase[] = []; public purchasesShortArray: Purchase[] = [];
private profileState$ = this.store.select(selectCustomerInfo);
private transactionsState$ = this.store.select(selectTransactions);
private transactionsInfoState$ = this.store.select(selectTransactionsInfo);
public dateFilterOptions = dateFilterOptions public dateFilterOptions = dateFilterOptions;
public defaultFilterType = 'currentMonth'; public defaultFilterType = 'currentMonth';
private startOfMonth = moment().startOf('month').format('YYYY-MM-DD HH:mm'); private startOfMonth = moment().startOf('month').format('YYYY-MM-DD HH:mm');
public filteredOfDatePurchases: Purchase[] = [] public filteredOfDatePurchases: Purchase[] = [];
constructor( constructor(
private wpJsonService: WpJsonService, private wpJsonService: WpJsonService,
private cookiesService: CookiesService, private cookiesService: CookiesService,
public dialog: MatDialog public dialog: MatDialog,
private store: Store
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
@ -42,32 +68,40 @@ export class OrdersComponent implements OnInit {
} }
dateFilter(dateFilter: IDateFilter) { dateFilter(dateFilter: IDateFilter) {
this.lastViewOrder = 3 this.lastViewOrder = 3;
switch (dateFilter.filterType) { switch (dateFilter.filterType) {
case 'between': case 'between':
this.filteredOfDatePurchases = this.purchases.filter((value) => { this.filteredOfDatePurchases = this.purchases.filter((value) => {
return moment(value.transactionCreateDate).isBetween(dateFilter.from, moment(dateFilter.to).add(1, 'day')) return moment(value.transactionCreateDate).isBetween(
}) dateFilter.from,
moment(dateFilter.to).add(1, 'day')
);
});
break; break;
case 'currentMonth': case 'currentMonth':
this.filteredOfDatePurchases = this.purchases.filter((value) => { this.filteredOfDatePurchases = this.purchases.filter((value) => {
return moment(value.transactionCreateDate).isAfter(this.startOfMonth) return moment(value.transactionCreateDate).isAfter(this.startOfMonth);
}) });
break; break;
case 'lastMonth': case 'lastMonth':
this.filteredOfDatePurchases = this.purchases.filter((value) => { this.filteredOfDatePurchases = this.purchases.filter((value) => {
return moment(value.transactionCreateDate).isBetween(moment(this.startOfMonth).subtract(1, 'month'), this.startOfMonth) return moment(value.transactionCreateDate).isBetween(
}) moment(this.startOfMonth).subtract(1, 'month'),
this.startOfMonth
);
});
break; break;
default: default:
this.filteredOfDatePurchases = this.purchases this.filteredOfDatePurchases = this.purchases;
break; break;
} }
this.purchasesShortArray = this.filteredOfDatePurchases.slice(0, this.lastViewOrder); this.purchasesShortArray = this.filteredOfDatePurchases.slice(
0,
this.lastViewOrder
);
} }
async getOrders() { async getOrders() {
@ -77,77 +111,49 @@ export class OrdersComponent implements OnInit {
this.deauthorization.emit(true); this.deauthorization.emit(true);
return; return;
} }
const customerInfo = await lastValueFrom(
this.wpJsonService.getCustomerInfo(
environment.systemId,
token,
environment.icardProxy
)
);
if (
customerInfo.customer_info?.errorCode === 'Customer_CustomerNotFound' ||
'Error' in customerInfo
) {
this.ordersLoadingStatus = false;
return;
}
const purchases: Purchase[] = (
await lastValueFrom(
this.wpJsonService.getTransactions(
environment.systemId,
token,
environment.icardProxy,
30
)
)
)[customerInfo.customer_info?.id];
const purchasesInfo: PurchaseInfo[] = ( combineLatest([this.transactionsState$, this.transactionsInfoState$])
await lastValueFrom( .pipe(distinctUntilChanged())
this.wpJsonService.getTransactionsInfo( .subscribe({
environment.systemId, next: ([transactions, transactionsInfo]) => {
token, if (transactions && transactionsInfo) {
environment.icardProxy, this.purchases = transactions
30 .map<Purchase>((purchase) => {
) return {
) ...purchase,
)['purchase']; more_info:
transactionsInfo.find(
(purchaseInfo: PurchaseInfo) =>
Number(purchaseInfo.number) === purchase.orderNumber
) || null,
};
})
.filter((purchase: Purchase) =>
[
'PayFromWallet',
'CancelPayFromWallet',
'RefillWallet',
].includes(purchase.transactionType)
);
this.purchases = purchases this.ordersLoadingStatus = false;
.map<Purchase>((purchase) => { this.dateFilter({ filterType: this.defaultFilterType });
// const id = purchase.ID.slice(0,36).toLowerCase(); }
// purchase.Transactions = transactions.filter((transaction) => { },
// const same = transaction.Purchase === id; });
// transaction.HasPurchase = same;
// return same;
// });
if (purchasesInfo) {
purchase.more_info =
purchasesInfo.find(
(purchaseInfo: PurchaseInfo) =>
Number(purchaseInfo.number) === purchase.orderNumber
) || null;
}
return purchase;
})
.filter((purchase: Purchase) =>
['PayFromWallet', 'CancelPayFromWallet', 'RefillWallet'].includes(
purchase.transactionType
)
);
this.dateFilter({filterType: this.defaultFilterType})
this.ordersLoadingStatus = false;
} }
getMoreOrders() { getMoreOrders() {
this.lastViewOrder += 4; this.lastViewOrder += 4;
this.purchasesShortArray = this.filteredOfDatePurchases.slice(0, this.lastViewOrder); this.purchasesShortArray = this.filteredOfDatePurchases.slice(
0,
this.lastViewOrder
);
} }
showPurchaseInfo(purchaseInfo: PurchaseInfo) { showPurchaseInfo(purchaseInfo: PurchaseInfo) {
const dialogRef = this.dialog.open(PurchaseInfoComponent, { const dialogRef = this.dialog.open(PurchaseInfoComponent, {
data: {purchaseInfo}, data: { purchaseInfo },
}); });
} }
} }

View File

@ -6,8 +6,11 @@ import {
Output, Output,
} from '@angular/core'; } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
import { Store } from '@ngrx/store';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { combineLatest } from 'rxjs';
import { SnackBarComponent } from 'src/app/components/snack-bar/snack-bar.component'; import { SnackBarComponent } from 'src/app/components/snack-bar/snack-bar.component';
import { ICardCustomer } from 'src/app/interface/data';
import { Order } from 'src/app/models/order'; import { Order } from 'src/app/models/order';
import { OrderProduct } from 'src/app/models/order-product'; import { OrderProduct } from 'src/app/models/order-product';
import { import {
@ -15,6 +18,12 @@ import {
ProductAmountAction, ProductAmountAction,
} from 'src/app/services/cart.service'; } from 'src/app/services/cart.service';
import { OrderService } from 'src/app/services/order.service'; import { OrderService } from 'src/app/services/order.service';
import {
selectCustomerCards,
selectCustomerWalletBalance,
} from 'src/app/state/profile/profile.reducer';
import * as ProfileActions from '../../state/profile/profile.actions';
import { CookiesService } from 'src/app/services/cookies.service';
@Component({ @Component({
selector: 'app-cart', selector: 'app-cart',
@ -30,18 +39,39 @@ export class CartComponent implements OnInit {
public visibleSidebar: boolean = false; public visibleSidebar: boolean = false;
public isFullScreen!: boolean; public isFullScreen!: boolean;
public width!: number; public width!: number;
public CardsCustomer!: ICardCustomer[];
public WalletBalanceCustomer!: number;
private CardsCustomer$ = this.store.select(selectCustomerCards);
private WalletBalanceCustomer$ = this.store.select(
selectCustomerWalletBalance
);
constructor( constructor(
private orderService: OrderService, private orderService: OrderService,
private cartService: CartService, private cartService: CartService,
private messageService: MessageService, private messageService: MessageService,
private _snackBar: MatSnackBar private _snackBar: MatSnackBar,
private store: Store,
private cookiesService: CookiesService
) {} ) {}
ngOnInit(): void { async ngOnInit() {
this.width = window.innerWidth; this.width = window.innerWidth;
this.changeDullScreenMode(); this.changeDullScreenMode();
this.loadCart(); await this.loadCart();
combineLatest([this.CardsCustomer$, this.WalletBalanceCustomer$]).subscribe(
{
next: ([cards, balance]) => {
this.CardsCustomer = cards!;
this.WalletBalanceCustomer = balance!;
if (cards && balance) this.loading = false;
},
error: (err) => {
console.error('Произошла ошибка!');
},
}
);
} }
// Изменение размера окна // Изменение размера окна
@ -72,27 +102,48 @@ export class CartComponent implements OnInit {
async loadCart(): Promise<void> { async loadCart(): Promise<void> {
this.loading = true; this.loading = true;
this.order = await this.orderService.getOrder(true); this.order = await this.orderService.getOrder(true);
const token = this.cookiesService.getItem('token');
if (!token) {
this._snackBar.open('Авторизуйтесь!', 'Ок');
this.userNotFound();
return;
}
if (this.order?.userData?.errorCode === 'Customer_CustomerNotFound') { if (this.order?.userData?.errorCode === 'Customer_CustomerNotFound') {
this.userNotFound() this.userNotFound();
return return;
} }
if (this.order) this.price = this.order.price; if (this.order) this.price = this.order.price;
this.loading = false; this.store.dispatch(ProfileActions.getProfile());
} }
userNotFound(event: null = null) { userNotFound(event: null = null) {
this.visibleSidebar = false this.visibleSidebar = false;
this._snackBar.open('Пользователь не найден в системе! Обратитесь к руководству', 'Ок') // this._snackBar.open('Пользователь не найден в системе! Обратитесь к руководству', 'Ок')
} }
removeFromCart(event: Event, guid: string): void { async removeFromCart(event: Event, guid: string) {
event.preventDefault(); event.preventDefault();
this.orderService.removeFromCart(guid); this.orderService.removeFromCart(guid);
await this.loadCart();
} }
confirmOrder(event: Event): void { confirmOrder(event: Event): void {
event.preventDefault(); event.preventDefault();
this.showAuthoriztion.emit(true); this.showAuthoriztion.emit(true);
if (!this.CardsCustomer[0].IsActivated || !this.CardsCustomer.length) {
this._snackBar.open(
'Ваша карта неактивна! Обратитесь к руководству',
'Ок'
);
return;
}
if (this.WalletBalanceCustomer < this.order.price) {
this._snackBar.open(
'На Вашей карте недостаточно средств для оформления заказа!',
'Ок'
);
return;
}
this.orderConfirmed = true; this.orderConfirmed = true;
// this.confirm.emit(); // this.confirm.emit();
} }
@ -117,7 +168,7 @@ export class CartComponent implements OnInit {
orderSubmitted(orderid: number) { orderSubmitted(orderid: number) {
this.visibleSidebar = false; this.visibleSidebar = false;
this._snackBar.open(`Заказ оформлен! Номер заказа: ${orderid}`, 'Ок') this._snackBar.open(`Заказ оформлен! Номер заказа: ${orderid}`, 'Ок');
} }
confirmClearCart() { confirmClearCart() {

View File

@ -16,6 +16,7 @@ import { MessagingService } from 'src/app/services/messaging.service';
import { CookiesService } from 'src/app/services/cookies.service'; import { CookiesService } from 'src/app/services/cookies.service';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import * as ConfigActions from '../../state/config/config.actions' import * as ConfigActions from '../../state/config/config.actions'
import { getProfile } from 'src/app/state/profile/profile.actions';
@Component({ @Component({
selector: 'app-main', selector: 'app-main',
@ -62,7 +63,8 @@ export class MainComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.appendAccount(); this.appendAccount();
this.store.dispatch(ConfigActions.getConfig()); // this.store.dispatch(ConfigActions.getConfig());
// this.store.dispatch(getProfile());
// this.checkRequestPermission() // this.checkRequestPermission()
} }

View File

@ -1,15 +1,12 @@
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Group, Modifier, ModifiersGroup, Product } from '../../interface/data'; import { Group, Modifier, ModifiersGroup, Product } from '../../interface/data';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { DialogService } from 'primeng/dynamicdialog'; import { DialogService } from 'primeng/dynamicdialog';
import { ProductModalComponent } from 'src/app/components/product-modal/product-modal.component'; import { ProductModalComponent } from 'src/app/components/product-modal/product-modal.component';
import { WpJsonService } from 'src/app/services/wp-json.service'; import { WpJsonService } from 'src/app/services/wp-json.service';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { lastValueFrom } from 'rxjs';
import { CartService } from 'src/app/services/cart.service'; import { CartService } from 'src/app/services/cart.service';
import { CookiesService } from 'src/app/services/cookies.service';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { MatSelectChange } from '@angular/material/select';
import { MatBottomSheet } from '@angular/material/bottom-sheet'; import { MatBottomSheet } from '@angular/material/bottom-sheet';
import { TerminalListComponent } from 'src/app/components/terminal-list/terminal-list.component'; import { TerminalListComponent } from 'src/app/components/terminal-list/terminal-list.component';
import { GetTerminalsService } from 'src/app/services/get-terminals.service'; import { GetTerminalsService } from 'src/app/services/get-terminals.service';

View File

@ -18,6 +18,10 @@ import { MessageService } from 'primeng/api';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
import { Store } from '@ngrx/store';
import { getProfile } from '../state/profile/profile.actions';
import * as ProfileActions from '../state/profile/profile.actions';
import { selectCustomerInfo } from '../state/profile/profile.reducer';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@ -30,7 +34,8 @@ export class OrderService {
private wpJsonService: WpJsonService, private wpJsonService: WpJsonService,
private jsonRpcService: JsonrpcService, private jsonRpcService: JsonrpcService,
private cookiesService: CookiesService, private cookiesService: CookiesService,
private messageService: MessageService private messageService: MessageService,
private store: Store
) {} ) {}
async getDeliveryTypes(): Promise<DeliveryType[]> { async getDeliveryTypes(): Promise<DeliveryType[]> {
@ -66,11 +71,8 @@ export class OrderService {
}); });
return this.order; return this.order;
} }
const additionalInfo = this.wpJsonService.getCustomerInfo( this.store.dispatch(ProfileActions.getProfile())
environment.systemId, const additionalInfo = this.store.select(selectCustomerInfo);
token,
environment.icardProxy
);
const tokenData = this.jsonRpcService.rpc( const tokenData = this.jsonRpcService.rpc(
{ {
@ -82,17 +84,23 @@ export class OrderService {
); );
const info = await lastValueFrom( const info = await lastValueFrom(
forkJoin([additionalInfo, tokenData, products]) forkJoin([tokenData, products])
); );
const customer_info = info[0]?.customer_info;
additionalInfo.subscribe({
this.order = new Order({ next: (value) => {
products: products, this.order = new Order({
userData: customer_info, products: products,
phone: info[1].data?.mobile_number, userData: value! as UserData,
token: token, phone: info[0].data?.mobile_number,
terminal_id: terminal.id, token: token,
}); terminal_id: terminal.id,
});
},
error: (err) => {
console.error(err);
}
})
} else if (this.order) { } else if (this.order) {
this.order.products.length = 0; this.order.products.length = 0;
} }
@ -101,11 +109,12 @@ export class OrderService {
} }
async getProducts(cart: Cart): Promise<OrderProduct[]> { async getProducts(cart: Cart): Promise<OrderProduct[]> {
const terminal = const terminal = JSON.parse(
JSON.parse(this.cookiesService.getItem('selectedTerminal') || 'null'); this.cookiesService.getItem('selectedTerminal') || 'null'
);
const products: OrderProduct[] = []; const products: OrderProduct[] = [];
if (!terminal) { if (!terminal) {
return products return products;
} }
const allData = await lastValueFrom( const allData = await lastValueFrom(
this.wpJsonService.getAllData(`${terminal.label}${terminal.id}`) this.wpJsonService.getAllData(`${terminal.label}${terminal.id}`)

View File

@ -3,11 +3,11 @@ import { createAction, props } from '@ngrx/store';
export const getConfig = createAction('[Config] Get'); export const getConfig = createAction('[Config] Get');
export const getSuccess = createAction( export const getSuccess = createAction(
'[Config] Get Success', '[Config] Get Success',
props<{ getSuccessResponse: any }>() props<{ getSuccessResponse: any }>()
); );
export const getFailure = createAction( export const getFailure = createAction(
'[Config] Get Failure', '[Config] Get Failure',
props<{ error: string }>() props<{ error: string }>()
); );

View File

@ -0,0 +1,15 @@
import { createAction, props } from '@ngrx/store';
export const getProfile = createAction('[Profile] Get Profile');
export const getTransactions = createAction('[Profile] Get Transactions');
export const getTransactionsInfo = createAction('[Profile] Get TransactionsInfo');
export const getSuccess = createAction(
'[Profile] Get Success',
props<{ getSuccessResponse: any }>()
);
export const getFailure = createAction(
'[Profile] Get Failure',
props<{ error: string }>()
);

View File

@ -0,0 +1,124 @@
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import * as ProfileActions from './profile.actions';
import { WpJsonService } from 'src/app/services/wp-json.service';
import { environment } from 'src/environments/environment';
import { CookiesService } from 'src/app/services/cookies.service';
import { catchError, exhaustMap, map, of, throwError } from 'rxjs';
@Injectable()
export class ProfileEffects {
constructor(
private actions$: Actions,
private wpJsonService: WpJsonService,
private cookiesService: CookiesService
) {}
getProfile$ = createEffect(() => {
const token = this.cookiesService.getItem('token');
return this.actions$.pipe(
ofType(ProfileActions.getProfile),
exhaustMap(() => {
return this.wpJsonService
.getCustomerInfo(
environment.systemId,
token || '',
environment.icardProxy
)
.pipe(
map((value) => {
if (
value.customer_info?.errorCode === 'Customer_CustomerNotFound'
) {
throw new Error('Пользователь не найден в системе! Обратитесь к руководству')
}
if ('Error' in value) {
throw new Error('Ошибка получения данных');
}
return {
getSuccessResponse: value,
};
}),
map((value) => ProfileActions.getSuccess(value as any)),
catchError((error) => {
return of(ProfileActions.getFailure({ error }));
})
);
})
);
});
getTransactions$ = createEffect(() => {
const token = this.cookiesService.getItem('token');
return this.actions$.pipe(
ofType(ProfileActions.getTransactions),
exhaustMap(() => {
return this.wpJsonService
.getTransactions(
environment.systemId,
token || '',
environment.icardProxy,
60
)
.pipe(
map((value) => {
if (
value.customer_info?.errorCode === 'Customer_CustomerNotFound'
) {
throw new Error('Пользователь не найден в системе! Обратитесь к руководству')
}
if ('Error' in value) {
throw new Error('Ошибка получения данных');
}
return {
getSuccessResponse: {
transactions: Object.values(value)[0]
},
};
}),
map((value) => ProfileActions.getSuccess(value as any)),
catchError((error) => {
return of(ProfileActions.getFailure({ error }));
})
);
})
);
});
getTransactionsInfo$ = createEffect(() => {
const token = this.cookiesService.getItem('token');
return this.actions$.pipe(
ofType(ProfileActions.getTransactionsInfo),
exhaustMap(() => {
return this.wpJsonService
.getTransactionsInfo(
environment.systemId,
token || '',
environment.icardProxy,
60
)
.pipe(
map((value) => {
if (
value.customer_info?.errorCode === 'Customer_CustomerNotFound'
) {
throw new Error('Пользователь не найден в системе! Обратитесь к руководству')
}
if ('Error' in value) {
throw new Error('Ошибка получения данных');
}
return {
getSuccessResponse: {
transactionsInfo: value.purchase
},
};
}),
map((value) => ProfileActions.getSuccess(value as any)),
catchError((error) => {
return of(ProfileActions.getFailure({ error }));
})
);
})
);
});
}

View File

@ -0,0 +1,81 @@
import {
createFeatureSelector,
createReducer,
createSelector,
on,
} from '@ngrx/store';
import { getFailure, getSuccess } from './profile.actions';
import { ICustomerInfo, Purchase, PurchaseInfo } from 'src/app/interface/data';
export interface ProfileState {
customer_info: ICustomerInfo | null;
transactions: Purchase[] | null;
transactionsInfo: PurchaseInfo[] | null;
}
export const initialProfileState: ProfileState = {
customer_info: null,
transactions: null,
transactionsInfo: null,
};
const _profileReducer = createReducer(
initialProfileState,
on(getSuccess, (state, { getSuccessResponse }) => {
return {
...state,
...getSuccessResponse,
};
}),
on(getFailure, (state, { error }) => {
console.error(error);
return state;
})
);
export function profileReducer(state: any, action: any) {
return _profileReducer(state, action);
}
export const selectProfileState =
createFeatureSelector<ProfileState>('profile');
export const selectCustomerInfo = createSelector(
selectProfileState,
(state) => {
return state.customer_info;
}
);
export const selectCustomerWalletBalance = createSelector(
selectProfileState,
(state) => {
return state?.customer_info?.walletBalances?.reduce<number>(
(previousValue, currentValue) => {
return previousValue + currentValue.balance;
},
0
);
}
);
export const selectCustomerCards = createSelector(
selectProfileState,
(state) => {
return state?.customer_info?.cards
}
);
export const selectTransactions = createSelector(
selectProfileState,
(state) => {
return state.transactions;
}
);
export const selectTransactionsInfo = createSelector(
selectProfileState,
(state) => {
return state.transactionsInfo;
}
);