Merge branch 'coffee-like-test' into coffee-like
This commit is contained in:
commit
97ce3c9e55
@ -1,16 +0,0 @@
|
||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# For the full list of supported browsers by the Angular framework, please see:
|
||||
# https://angular.io/guide/browser-support
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 Safari major versions
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
||||
@ -4,7 +4,7 @@
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"coffee-like-test": {
|
||||
"coffee-like": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
@ -35,7 +35,7 @@
|
||||
"src/sw-custom.js"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/purple-green.css",
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
"node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css",
|
||||
"node_modules/primeicons/primeicons.css",
|
||||
"node_modules/primeng/resources/primeng.min.css",
|
||||
@ -84,10 +84,10 @@
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "coffee-like-test:build:production"
|
||||
"browserTarget": "coffee-like:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "coffee-like-test:build:development",
|
||||
"browserTarget": "coffee-like:build:development",
|
||||
"proxyConfig": "proxy.confi.json"
|
||||
}
|
||||
},
|
||||
@ -96,7 +96,7 @@
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "coffee-like-test:build"
|
||||
"browserTarget": "coffee-like:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
|
||||
11444
angular/package-lock.json
generated
11444
angular/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "coffee-like-test",
|
||||
"name": "coffee-like",
|
||||
"version": "0.0.2",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
@ -10,43 +10,44 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^14.0.0",
|
||||
"@angular/cdk": "^14.2.1",
|
||||
"@angular/common": "^14.0.0",
|
||||
"@angular/compiler": "^14.0.0",
|
||||
"@angular/core": "^14.0.0",
|
||||
"@angular/fire": "^7.4.1",
|
||||
"@angular/forms": "^14.0.0",
|
||||
"@angular/material": "^14.2.1",
|
||||
"@angular/platform-browser": "^14.0.0",
|
||||
"@angular/platform-browser-dynamic": "^14.0.0",
|
||||
"@angular/router": "^14.0.0",
|
||||
"@angular/service-worker": "^14.0.0",
|
||||
"@fortawesome/angular-fontawesome": "^0.11.1",
|
||||
"@angular/animations": "^15.2.9",
|
||||
"@angular/cdk": "^15.2.9",
|
||||
"@angular/common": "^15.2.9",
|
||||
"@angular/compiler": "^15.2.9",
|
||||
"@angular/core": "^15.2.9",
|
||||
"@angular/fire": "^7.5.0",
|
||||
"@angular/forms": "^15.2.9",
|
||||
"@angular/material": "^15.2.9",
|
||||
"@angular/platform-browser": "^15.2.9",
|
||||
"@angular/platform-browser-dynamic": "^15.2.9",
|
||||
"@angular/router": "^15.2.9",
|
||||
"@angular/service-worker": "^15.2.9",
|
||||
"@fortawesome/angular-fontawesome": "^0.12.1",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.2.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.2.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.0",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/web": "^0.0.99",
|
||||
"angular-moment-timezone": "^1.7.1",
|
||||
"barcode-2-svg": "^0.3.3",
|
||||
"firebase": "^9.9.3",
|
||||
"google-libphonenumber": "^3.2.30",
|
||||
"jsbarcode": "^3.11.5",
|
||||
"libphonenumber-js": "^1.10.28",
|
||||
"ng-qrcode": "^7.0.0",
|
||||
"ng-qrcode": "^8.0.1",
|
||||
"ngx-mat-intl-tel-input": "^5.0.0",
|
||||
"ngx-sharebuttons": "^11.0.0",
|
||||
"primeicons": "^5.0.0",
|
||||
"primeng": "^14.0.1",
|
||||
"primeng": "^15.4.1",
|
||||
"rxjs": "~7.5.0",
|
||||
"tslib": "^2.3.0",
|
||||
"uuid": "^8.3.2",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^14.0.6",
|
||||
"@angular/cli": "~14.0.6",
|
||||
"@angular/compiler-cli": "^14.0.0",
|
||||
"@angular-devkit/build-angular": "^15.2.8",
|
||||
"@angular/cli": "~15.2.8",
|
||||
"@angular/compiler-cli": "^15.2.9",
|
||||
"@types/google-libphonenumber": "^7.4.23",
|
||||
"@types/jasmine": "~4.0.0",
|
||||
"jasmine-core": "~4.1.0",
|
||||
@ -55,6 +56,6 @@
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.0.0",
|
||||
"karma-jasmine-html-reporter": "~1.7.0",
|
||||
"typescript": "~4.7.2"
|
||||
"typescript": "4.8"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1,2 @@
|
||||
<router-outlet></router-outlet>
|
||||
<p-toast position="top-center"></p-toast>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { RouteConfigLoadStart, Router } from '@angular/router';
|
||||
import { AuthService } from './services/auth.service';
|
||||
import { CookiesService } from './services/cookies.service';
|
||||
import { JsonrpcService, RpcService } from './services/jsonrpc.service';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@ -10,12 +10,13 @@ import { lastValueFrom } from 'rxjs';
|
||||
styleUrls: ['./app.component.scss'],
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
title = 'Coffee Like Test';
|
||||
title = 'Coffee Like';
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private cookiesService: CookiesService,
|
||||
private jsonRpcService: JsonrpcService
|
||||
private jsonRpcService: JsonrpcService,
|
||||
private authService: AuthService,
|
||||
) {
|
||||
this.router.events.subscribe((x) => {
|
||||
if (x instanceof RouteConfigLoadStart && x.route.path === '') {
|
||||
@ -48,6 +49,9 @@ export class AppComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.authService.authorized) {
|
||||
this.authService.getUserInfo();
|
||||
}
|
||||
this.getAdditionalInfo().subscribe({
|
||||
next: (value) => {
|
||||
this.cookiesService.setCookie('presentation-option', value?.data?.interface_id || 'default')
|
||||
|
||||
@ -16,7 +16,6 @@ import { HttpClientModule } from '@angular/common/http';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { DialogService } from 'primeng/dynamicdialog';
|
||||
import { BonusProgramComponent } from './pages/account/bonus-program/bonus-program.component';
|
||||
import { OrdersComponent } from './pages/account/orders/orders.component';
|
||||
import { OrderInfoComponent } from './components/order-info/order-info.component';
|
||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||
import { environment } from '../environments/environment';
|
||||
@ -62,7 +61,6 @@ import { DirectivesModule } from './directives/directives.module';
|
||||
AccountComponent,
|
||||
ExitComponent,
|
||||
BonusProgramComponent,
|
||||
OrdersComponent,
|
||||
OrderInfoComponent,
|
||||
FooterButtonsComponent,
|
||||
UserDataComponent,
|
||||
|
||||
@ -2,13 +2,12 @@ import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FocusNextInputDirective } from './focus-next-input.directive';
|
||||
import { DownloadAppDirective } from './download-app.directive';
|
||||
import { UpdateOutlineGapDirective } from './update-outline-gap.directive';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule
|
||||
],
|
||||
declarations: [FocusNextInputDirective, DownloadAppDirective, UpdateOutlineGapDirective],
|
||||
exports: [FocusNextInputDirective, DownloadAppDirective, UpdateOutlineGapDirective]
|
||||
declarations: [FocusNextInputDirective, DownloadAppDirective],
|
||||
exports: [FocusNextInputDirective, DownloadAppDirective]
|
||||
})
|
||||
export class DirectivesModule { }
|
||||
|
||||
@ -5,8 +5,9 @@ import {
|
||||
OnInit,
|
||||
Renderer2,
|
||||
} from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { getTypeDevice, pwaInstalled } from 'src/app/utils';
|
||||
|
||||
@Directive({
|
||||
selector: '[appDownloadApp]',
|
||||
@ -19,14 +20,21 @@ export class DownloadAppDirective implements OnInit {
|
||||
private messageService: MessageService,
|
||||
public renderer: Renderer2,
|
||||
private el: ElementRef,
|
||||
private _snackBar: MatSnackBar,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getTypeDevice();
|
||||
getTypeDevice();
|
||||
if (this.deviceType === 'ios') {
|
||||
this.el.nativeElement.style.display = 'block';
|
||||
}
|
||||
this.checkInstalled();
|
||||
}
|
||||
|
||||
async checkInstalled(): Promise<void> {
|
||||
if (window.matchMedia('(display-mode: standalone)').matches
|
||||
|| await pwaInstalled()) {
|
||||
this.el.nativeElement.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('window:beforeinstallprompt', ['$event'])
|
||||
@ -47,41 +55,27 @@ export class DownloadAppDirective implements OnInit {
|
||||
this.el.nativeElement.style.display = 'none';
|
||||
}
|
||||
|
||||
getTypeDevice() {
|
||||
const userAgent = window.navigator.userAgent.toLowerCase();
|
||||
const ios = /iphone|ipod|ipad/.test(userAgent);
|
||||
this.deviceType = ios ? 'ios' : 'android';
|
||||
}
|
||||
|
||||
@HostListener('click', ['$event'])
|
||||
downloadApp(event: MouseEvent) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (this.deviceType === 'ios') {
|
||||
this._snackBar.open(`Для установки нажмите на кнопку поделиться в Вашем браузере и выберите пункт 'На экран «Домой»'`, '', {
|
||||
duration: 5000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
async downloadApp(event: MouseEvent) {
|
||||
if (!this.deferredPrompt) {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: 'Не поддерживается в Вашем браузере!',
|
||||
});
|
||||
this._snackBar.open('Не поддерживается в Вашем браузере!', '', {
|
||||
duration: 3000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.deferredPrompt.prompt();
|
||||
this.deferredPrompt.userChoice.then((res: any) => {
|
||||
if (res.outcome === 'accepted') {
|
||||
this._snackBar.open('Спасибо за установку!', '', {
|
||||
duration: 3000,
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'success',
|
||||
summary: 'Спасибо за установку!',
|
||||
});
|
||||
}
|
||||
this.deferredPrompt = null;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -9,10 +9,16 @@ export class FocusNextInputDirective {
|
||||
constructor(private renderer: Renderer2) { }
|
||||
|
||||
ngOnInit() {
|
||||
// TODO:
|
||||
// 1: don't need to listen all events
|
||||
// 2: not working in safari
|
||||
this.eventEmitter.subscribe(elementId => {
|
||||
try {
|
||||
this.renderer.selectRootElement(elementId).focus();
|
||||
this.renderer.selectRootElement(elementId).click();
|
||||
const element = this.renderer.selectRootElement(elementId)
|
||||
setTimeout(() => {
|
||||
element.focus();
|
||||
element.click();
|
||||
}, 0);
|
||||
} catch (ex) {
|
||||
(document.activeElement as HTMLElement).blur();
|
||||
// If the element doesn't exist or if the element disappears when this called then no need to do anything
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
import { AfterViewInit, Directive } from '@angular/core';
|
||||
import { MatFormField } from '@angular/material/form-field';
|
||||
|
||||
// TODO: this is temporary workaround, upgrade angular to 15+, where input labels fork fine
|
||||
@Directive({
|
||||
selector: 'mat-form-field[appearance=outline]',
|
||||
})
|
||||
export class UpdateOutlineGapDirective implements AfterViewInit {
|
||||
constructor(private formField: MatFormField) {
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
document.fonts.ready.then(() => {
|
||||
this.formField.updateOutlineGap();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -179,3 +179,52 @@ export interface UserData {
|
||||
city: string;
|
||||
phone: string | null;
|
||||
}
|
||||
|
||||
export interface UserInfoCategory {
|
||||
id: string;
|
||||
isActive: boolean;
|
||||
isDefaultForNewGuests: boolean;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface UserInfoWallet {
|
||||
id: string;
|
||||
name: string;
|
||||
programType: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface UserInfoWalletBalance {
|
||||
balance: number;
|
||||
wallet: UserInfoWallet;
|
||||
}
|
||||
|
||||
export interface CurrentInfo {
|
||||
current_cashback: number;
|
||||
current_level: number;
|
||||
}
|
||||
|
||||
export interface LastPurchase {
|
||||
last_purchase_date: string;
|
||||
last_purchase_sum: number;
|
||||
}
|
||||
|
||||
export interface NextLevel {
|
||||
cashback: number;
|
||||
next_level: number;
|
||||
sum_for_next_level: number;
|
||||
}
|
||||
|
||||
export interface UserInfo {
|
||||
current_level_and_cashback?: CurrentInfo;
|
||||
last_purchase?: LastPurchase;
|
||||
id: string;
|
||||
phone: string;
|
||||
walletBalances: number;
|
||||
next_level: NextLevel;
|
||||
}
|
||||
|
||||
export interface ResponseError {
|
||||
code: number;
|
||||
msg: string;
|
||||
}
|
||||
|
||||
@ -53,12 +53,14 @@ export class RefSystemComponent implements OnInit {
|
||||
copyUrl() {
|
||||
navigator.clipboard.writeText(this.refUrl)
|
||||
.then(() => {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'custom',
|
||||
summary: 'Ссылка скопирована!',
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: 'Произошла ошибка!',
|
||||
|
||||
@ -85,6 +85,7 @@ export class LoginComponent implements OnInit, AfterViewInit {
|
||||
const data = this.phoneForm.value;
|
||||
this.isShowNumber = false;
|
||||
if (this.timeLeft) {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'custom',
|
||||
summary: `Отправить повторно можно через ${this.timeLeft}с`,
|
||||
|
||||
@ -61,6 +61,7 @@ export class MainComponent implements OnInit {
|
||||
const userAgent = window.navigator.userAgent.toLowerCase();
|
||||
const ios = /iphone|ipod|ipad/.test(userAgent);
|
||||
if (ios) {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'custom',
|
||||
summary: `Чтобы получать уведомления, добавьте карту в Apple Wallet`,
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
<h2>Ваш предыдущий заказ</h2>
|
||||
<div class="info-order">
|
||||
<ng-container *ngIf="lastOrder">
|
||||
<p class="flex"><span>Дата: </span>
|
||||
<span *ngIf="!loading">{{(lastOrder?.transactionCreateDate | date:'dd.MM.yyyyг.') || 'Данные не найдены'}}</span>
|
||||
<ng-container *ngIf="loading">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="spinner; context: { $implicit: 24 }"
|
||||
></ng-container>
|
||||
</ng-container>
|
||||
<span *ngIf="!loading">{{lastOrder!.last_purchase_date}}</span>
|
||||
</p>
|
||||
<p class="flex"><span>На сумму: </span>
|
||||
<span *ngIf="!loading">{{lastOrder?.orderSum ? lastOrder?.orderSum + ' ₽' : 'Данные не найдены'}}</span>
|
||||
<ng-container *ngIf="loading">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="spinner; context: { $implicit: 24 }"
|
||||
></ng-container>
|
||||
</ng-container>
|
||||
<span *ngIf="!loading">{{lastOrder?.last_purchase_sum}}</span>
|
||||
</p>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!lastOrder">
|
||||
<p class="flex">
|
||||
<span>Данные не найдены</span>
|
||||
</p>
|
||||
</ng-container>
|
||||
</div>
|
||||
<a href="https://promo.coffee-like.com/?utm_medium=prilozhenie">
|
||||
<img src="./assets/970х250_3.png" alt="" width="100%" />
|
||||
</a>
|
||||
<a href="https://yandex.ru/profile/151770398186" target="_blank">
|
||||
<button class="evaluate-order">Оценить заказ</button>
|
||||
</a>
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
padding: 24px 16px 0px;
|
||||
|
||||
& > h2 {
|
||||
font-family: Montserrat;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 15px;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Purchase } from 'src/app/interface/data';
|
||||
import { LastPurchase } from 'src/app/interface/data';
|
||||
|
||||
@Component({
|
||||
selector: 'app-last-order[lastOrder]',
|
||||
@ -7,12 +7,11 @@ import { Purchase } from 'src/app/interface/data';
|
||||
styleUrls: ['./last-order.component.scss']
|
||||
})
|
||||
export class LastOrderComponent implements OnInit {
|
||||
@Input() lastOrder!: Purchase;
|
||||
@Input() lastOrder?: LastPurchase;
|
||||
@Input() loading!: boolean;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
<li class="menu-item">
|
||||
<ng-content></ng-content>
|
||||
</li>
|
||||
@ -0,0 +1,20 @@
|
||||
.menu-item {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.1s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--main-color_hover);
|
||||
}
|
||||
|
||||
.title {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.icon {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
import { Component, Input, HostListener, } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: 'menu-item',
|
||||
templateUrl: './menu_item.component.html',
|
||||
styleUrls: ['./menu_item.component.scss'],
|
||||
})
|
||||
export class MenuItemComponent {
|
||||
@Input()
|
||||
handler?: (e: any) => void;
|
||||
|
||||
@HostListener('click', ['$event'])
|
||||
onClick(e: any) {
|
||||
if (this.handler) {
|
||||
this.handler(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,51 @@
|
||||
<div class="container">
|
||||
<mat-icon style="cursor: pointer;" aria-hidden="false" aria-label="Назад" fontIcon="arrow_back_ios" class="back-arrow" (click)="back()"></mat-icon>
|
||||
<div class="block">
|
||||
<h1 class="title">{{title}}</h1>
|
||||
</div>
|
||||
<div class="block">
|
||||
<div class="back">
|
||||
<mat-icon *ngIf="backEvent" style="cursor: pointer;" aria-hidden="false" aria-label="Назад" fontIcon="arrow_back_ios" class="back-arrow" (click)="back()"></mat-icon>
|
||||
</div>
|
||||
<!-- <div class="plug"></div> -->
|
||||
<div *ngIf="showDropdown" class="backdrop" (click)="closeMenu()"></div>
|
||||
<div *ngIf="showMenu" class="menu">
|
||||
<button mat-button (click)="toggleMenu()">
|
||||
<span class="material-icons" style="color: white !important">
|
||||
more_horiz
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div class="menu__dropdown" *ngIf="showDropdown">
|
||||
<ul>
|
||||
<menu-item [handler]="handler(aboutApp)">
|
||||
<span class="material-icons icon" style="color: var(--main-color)">
|
||||
info
|
||||
</span>
|
||||
<span class="item_title">О приложении</span>
|
||||
</menu-item>
|
||||
<menu-item [handler]="handler(addToWallet)">
|
||||
<img src="./assets/apple_wallet.svg" class="icon" width="24" height="24" />
|
||||
<span class="item_title">Добавить карточку в Wallet</span>
|
||||
</menu-item>
|
||||
<menu-item [handler]="handler(logout)">
|
||||
<span class="material-icons" style="color: var(--main-color)">
|
||||
logout
|
||||
</span>
|
||||
<span class="item_title">Выйти</span>
|
||||
</menu-item>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper" *ngIf="!showMenu">
|
||||
<button
|
||||
mat-button
|
||||
*ngIf="!messagingService.checkRequestPermission()"
|
||||
(click)="messagingService.requestPermission()"
|
||||
class="notification"
|
||||
>
|
||||
<img src="./assets/notification.svg" alt="notification" />
|
||||
</button>
|
||||
<button mat-stroked-button appDownloadApp>Установить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,18 +1,33 @@
|
||||
:host {
|
||||
width: 100%;
|
||||
|
||||
.notification {
|
||||
img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
padding: 12px 16px;
|
||||
width: 100%;
|
||||
background: var(--button-color);
|
||||
color: var(--button-text-color);
|
||||
display: grid;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
.block {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
grid-template-columns: auto auto auto;
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
.back {
|
||||
width: 40px;
|
||||
}
|
||||
.back-arrow {
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
@ -25,10 +40,44 @@
|
||||
visibility: hidden;
|
||||
}
|
||||
.title {
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
margin: 0;
|
||||
margin: 0 auto;
|
||||
|
||||
}
|
||||
.wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 40px;
|
||||
}
|
||||
.menu {
|
||||
position: relative;
|
||||
|
||||
.menu__dropdown {
|
||||
z-index: 110;
|
||||
position: fixed;
|
||||
background-color: white;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
right: 20px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0px 2px 5px -3px black;
|
||||
|
||||
.item_title {
|
||||
color: var(--text-color);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
.backdrop {
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
width: 100vw;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { MessagingService } from 'src/app/services/messaging.service';
|
||||
import { getTypeDevice, DeviceType } from 'src/app/utils';
|
||||
import { AppleWalletService } from 'src/app/services/apple-wallet.service';
|
||||
import { CookiesService } from 'src/app/services/cookies.service';
|
||||
import { MatBottomSheet } from '@angular/material/bottom-sheet';
|
||||
import { ExitComponent } from 'src/app/components/exit/exit.component';
|
||||
import { AuthService } from 'src/app/services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-navbar[title]',
|
||||
@ -7,15 +14,69 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
})
|
||||
export class NavbarComponent implements OnInit {
|
||||
@Input() title: string = 'Название не задано'
|
||||
@Output() backEvent = new EventEmitter<null>();
|
||||
@Input() backEvent?: () => void;
|
||||
|
||||
constructor() { }
|
||||
showMenu: boolean = false;
|
||||
showDropdown: boolean = false;
|
||||
|
||||
constructor(
|
||||
public messagingService: MessagingService,
|
||||
private appleWalletService: AppleWalletService,
|
||||
private cookiesService: CookiesService,
|
||||
private _bottomSheet: MatBottomSheet,
|
||||
private authService: AuthService,
|
||||
) { }
|
||||
|
||||
handler(cb: () => void): (e: any) => void {
|
||||
return (e: any) => {
|
||||
cb();
|
||||
this.closeMenu();
|
||||
}
|
||||
}
|
||||
|
||||
aboutApp() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
const deviceType = getTypeDevice();
|
||||
|
||||
this.showMenu = deviceType === DeviceType.ios;
|
||||
}
|
||||
|
||||
toggleMenu() {
|
||||
this.showDropdown = !this.showDropdown;
|
||||
}
|
||||
|
||||
openMenu() {
|
||||
this.showDropdown = true;
|
||||
}
|
||||
|
||||
closeMenu = () => {
|
||||
this.showDropdown = false;
|
||||
}
|
||||
|
||||
back() {
|
||||
this.backEvent.emit(null)
|
||||
if (this.backEvent) {
|
||||
this.backEvent();
|
||||
}
|
||||
}
|
||||
|
||||
addToWallet = () => {
|
||||
this.appleWalletService.addCardToWallet();
|
||||
}
|
||||
|
||||
deleteToken = (): void => {
|
||||
this.cookiesService.logout();
|
||||
}
|
||||
|
||||
logout = () => {
|
||||
const bottomSheet = this._bottomSheet.open(ExitComponent);
|
||||
bottomSheet.afterDismissed().subscribe({
|
||||
next: (val) => {
|
||||
if (val) {
|
||||
this.authService.logout();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ export class SocialMediaButtonsComponent implements OnInit {
|
||||
public links: ISocialMediaLink[] = [
|
||||
{
|
||||
label: 'Инстаграм',
|
||||
url: 'https://www.instagram.com/',
|
||||
url: 'https://instagram.com/coffeelike_com?igshid=MzRlODBiNWFlZA==',
|
||||
imgUrl: '/assets/social-media-icons/instagram.svg'
|
||||
},
|
||||
{
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { IndexComponent } from './components/index/index.component';
|
||||
import { GuestCardComponent } from './pages/guest-card/guest-card.component';
|
||||
import { AuthGuard } from 'src/app/guards/auth-guard.guard';
|
||||
import { LoginComponent } from './pages/login/login.component';
|
||||
|
||||
@ -5,6 +5,7 @@ import { IndexComponent } from './components/index/index.component';
|
||||
import { FooterComponent } from './components/footer/footer.component';
|
||||
import { SocialMediaButtonsComponent } from './components/social-media-buttons/social-media-buttons.component';
|
||||
import { NavbarComponent } from './components/navbar/navbar.component';
|
||||
import { MenuItemComponent } from './components/navbar/menu_item.component';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { GuestCardComponent } from './pages/guest-card/guest-card.component';
|
||||
import { QrCodeModule } from 'ng-qrcode';
|
||||
@ -20,6 +21,8 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { DirectivesModule } from 'src/app/directives/directives.module';
|
||||
import { LoyalityProgramComponent } from './pages/loyality-program/loyality-program.component';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { ToastModule } from 'primeng/toast';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -32,9 +35,11 @@ import {MatButtonModule} from '@angular/material/button';
|
||||
LastOrderComponent,
|
||||
InviteFriendsComponent,
|
||||
LoginComponent,
|
||||
LoyalityProgramComponent
|
||||
LoyalityProgramComponent,
|
||||
MenuItemComponent,
|
||||
],
|
||||
imports: [
|
||||
ToastModule,
|
||||
CommonModule,
|
||||
DefaultOptionRoutingModule,
|
||||
MatIconModule,
|
||||
|
||||
@ -1,92 +1,44 @@
|
||||
<app-navbar title="Карта гостя" (backEvent)="logout()"></app-navbar>
|
||||
|
||||
<ng-container *ngIf="!this.loyaltyProgram.purchaseData.$loading && customerInfo">
|
||||
<app-navbar title="Карта гостя" [backEvent]="showBack ? logout : undefined"></app-navbar>
|
||||
<ng-container *ngIf="!authService.loading && authService.userInfo">
|
||||
<div class="guest-card">
|
||||
<div class="top-info">
|
||||
<div class="top-info__level">
|
||||
<p id="level">Уровень {{ loyaltyProgram.currentLvl }}</p>
|
||||
<p id="level-percent">Кэшбек {{ loyaltyProgram.currentLvlPeriod.percent }}%</p>
|
||||
<p id="level">Уровень {{ authService.userInfo.current_level_and_cashback?.current_level ?? '--' }}</p>
|
||||
<p id="level-percent">Кэшбек {{ authService.currentLvlPeriod.percent }}%</p>
|
||||
</div>
|
||||
<p class="top-info__bonus">
|
||||
{{ Math.floor(loyaltyProgram.getBalanceAmount(customerInfo?.walletBalances)) }}
|
||||
{{ Math.floor(authService.userInfo.walletBalances) }}
|
||||
бонусов
|
||||
</p>
|
||||
</div>
|
||||
<div class="guest-card__qr" (click)="qrCodeClick()">
|
||||
<ng-container *ngIf="customerInfo">
|
||||
<qr-code
|
||||
[value]="customerInfo?.phone?.substr(1) || 'Данные не найдены'"
|
||||
[value]="authService.userInfo?.phone?.substr(1) || 'Данные не найдены'"
|
||||
[margin]="0"
|
||||
[size]="qrCodeSize"
|
||||
errorCorrectionLevel="H"
|
||||
></qr-code>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!customerInfo">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="spinner; context: { $implicit: 85 }"
|
||||
></ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="guest-card__user-description">
|
||||
<p>
|
||||
<ng-container *ngIf="loyaltyProgram.currentLvlPeriod.end">
|
||||
Осталось купить на сумму
|
||||
<span class="price">{{
|
||||
loyaltyProgram.currentLvlPeriod.end -
|
||||
(loyaltyProgram.purchaseData.currentAmount || 0) +
|
||||
1
|
||||
authService.userInfo.next_level.sum_for_next_level
|
||||
}}</span>
|
||||
рублей, тогда кэшбек будет
|
||||
<span class="percent"
|
||||
>{{ loyaltyProgram.getNextLevel().percent }}%</span
|
||||
>
|
||||
<span class="percent">{{ authService.userInfo.next_level.cashback }}%</span>
|
||||
с
|
||||
{{
|
||||
loyaltyProgram.purchaseData.currentPeriod[1]
|
||||
authService.currentPeriod[1]
|
||||
.locale("ru")
|
||||
.format("D MMMM")
|
||||
}}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!loyaltyProgram.currentLvlPeriod.end">
|
||||
У Вас последний уровень бонусной программы. Процент начисляемых
|
||||
бонусов:
|
||||
{{ loyaltyProgram.currentLvlPeriod.percent }}%
|
||||
</ng-container>
|
||||
</p>
|
||||
</div>
|
||||
<span id="bonuses-condition"></span>
|
||||
|
||||
<!-- <div class="guest-card__level-info"> -->
|
||||
<!-- <ng-container *ngIf="!purchaseData.$loading"> -->
|
||||
<!-- <ng-container *ngIf="currentLvlPeriod.end"> -->
|
||||
<!-- <\!-- <input -\-> -->
|
||||
<!-- <\!-- type="range" -\-> -->
|
||||
<!-- <\!-- [value]="purchaseData.currentAmount" -\-> -->
|
||||
<!-- <\!-- [min]="currentLvlPeriod.start" -\-> -->
|
||||
<!-- <\!-- [max]="currentLvlPeriod.end" -\-> -->
|
||||
<!-- <\!-- [step]="0.01" -\-> -->
|
||||
<!-- <\!-- disabled="true" -\-> -->
|
||||
<!-- <\!-- [ngStyle]="{ -\-> -->
|
||||
<!-- <\!-- 'background-size': ((purchaseData.currentAmount! - currentLvlPeriod.start) / (currentLvlPeriod.end - currentLvlPeriod.start + 1)) * 100 + '% 100%' -\-> -->
|
||||
<!-- <\!-- }" -\-> -->
|
||||
<!-- <\!-- /> -\-> -->
|
||||
<!-- </ng-container> -->
|
||||
<!-- <ng-container *ngIf="!currentLvlPeriod.end"> -->
|
||||
<!-- <h2> -->
|
||||
<!-- У Вас последний уровень бонусной программы. Процент начисляемых бонусов: {{currentLvlPeriod.percent}}% -->
|
||||
<!-- </h2> -->
|
||||
<!-- </ng-container> -->
|
||||
<!-- </ng-container> -->
|
||||
<!-- <ng-container *ngIf="purchaseData.$loading"> -->
|
||||
<!-- <ng-container -->
|
||||
<!-- *ngTemplateOutlet="spinner; context: { $implicit: 48 }" -->
|
||||
<!-- ></ng-container> -->
|
||||
<!-- </ng-container> -->
|
||||
|
||||
<!-- </div> -->
|
||||
<app-last-order
|
||||
[lastOrder]="lastPurchase"
|
||||
[loading]="loyaltyProgram.purchaseData.$loading"
|
||||
[lastOrder]="authService.userInfo?.last_purchase"
|
||||
[loading]="authService.loading"
|
||||
></app-last-order>
|
||||
<a class="guest-card__loyalty-program" routerLink="loyality-program"
|
||||
>Подробнее о правилах <br />
|
||||
@ -95,12 +47,12 @@
|
||||
</div>
|
||||
<app-footer></app-footer>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!customerInfo && !loyaltyProgram.purchaseData.$loading">
|
||||
<ng-container *ngIf="!authService.loading && !authService.userInfo">
|
||||
<div class="not-found">
|
||||
<p>Данные недоступны, попробуйте позже</p>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="loyaltyProgram.purchaseData.$loading">
|
||||
<ng-container *ngIf="authService.loading">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="spinner; context: { $implicit: 85 }"
|
||||
></ng-container>
|
||||
|
||||
@ -104,7 +104,6 @@
|
||||
|
||||
h2 {
|
||||
font-style: normal;
|
||||
font-family: Montserrat;
|
||||
font-weight: 700;
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
|
||||
@ -1,16 +1,11 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatBottomSheet } from '@angular/material/bottom-sheet';
|
||||
import { Router } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ExitComponent } from 'src/app/components/exit/exit.component';
|
||||
import { CookiesService } from 'src/app/services/cookies.service';
|
||||
import { WpJsonService } from 'src/app/services/wp-json.service';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import moment from 'moment';
|
||||
import { Moment, Purchase, lvlPeriod } from 'src/app/interface/data';
|
||||
import { lvlPeriods } from 'src/app/app.constants';
|
||||
import { LoyaltyProgramService } from 'src/app/services/loyalty-program.service';
|
||||
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { MessagingService } from 'src/app/services/messaging.service';
|
||||
import { DeviceType, getTypeDevice } from 'src/app/utils';
|
||||
import { AuthService } from 'src/app/services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-guest-card',
|
||||
@ -20,46 +15,41 @@ import { LoyaltyProgramService } from 'src/app/services/loyalty-program.service'
|
||||
export class GuestCardComponent implements OnInit {
|
||||
public qrCodeSize: number = 85;
|
||||
private isQrCodeClicked: boolean = false;
|
||||
public customerInfo!: any;
|
||||
public purchases!: Purchase[];
|
||||
public lastPurchase!: Purchase;
|
||||
public Math: Math = Math;
|
||||
public showBack: boolean = false;
|
||||
|
||||
constructor(
|
||||
private _bottomSheet: MatBottomSheet,
|
||||
public cookiesService: CookiesService,
|
||||
private router: Router,
|
||||
private wpJsonService: WpJsonService,
|
||||
public loyaltyProgram: LoyaltyProgramService
|
||||
private messagingService: MessagingService,
|
||||
private messageService: MessageService,
|
||||
public authService: AuthService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
const token = this.cookiesService.getItem('token');
|
||||
this.loyaltyProgram.purchaseData.$loading = true;
|
||||
this.wpJsonService
|
||||
.getCustomerInfo(
|
||||
environment.systemId,
|
||||
token || '',
|
||||
environment.icardProxy
|
||||
)
|
||||
.subscribe({
|
||||
next: (value) => {
|
||||
this.customerInfo = value.customer_info;
|
||||
this.cookiesService.setCookie('phone-number', this.customerInfo?.phone?.substr(2))
|
||||
this.getPurchases().subscribe((value) => {
|
||||
this.purchases = this.loyaltyProgram.filterPurchases(value[this.customerInfo?.id])
|
||||
this.lastPurchase = this.loyaltyProgram.getLastPurchase(this.purchases)
|
||||
this.showBack = getTypeDevice() === DeviceType.android;
|
||||
|
||||
this.loyaltyProgram.setLastPurchases(this.purchases)
|
||||
this.loyaltyProgram.setCurrentPurchases(this.purchases)
|
||||
this.requestPermission();
|
||||
}
|
||||
|
||||
const currentAmount = this.loyaltyProgram.purchaseData.currentAmount || 0
|
||||
this.loyaltyProgram.setCurrentLvl(currentAmount)
|
||||
// this.currentLvlPeriod = this.lvlPeriods.find((item) => item.start <= currentAmount && currentAmount <= (item.end || Infinity))!
|
||||
this.loyaltyProgram.purchaseData.$loading = false;
|
||||
});
|
||||
},
|
||||
requestPermission() {
|
||||
const userAgent = window.navigator.userAgent.toLowerCase();
|
||||
const ios = /iphone|ipod|ipad/.test(userAgent);
|
||||
if (ios) {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'custom',
|
||||
summary: `Чтобы получать уведомления, добавьте карту в Apple Wallet`,
|
||||
life: 5000,
|
||||
});
|
||||
|
||||
// var permissionData = window.safari.pushNotification.permission('web.com.example.domain');
|
||||
// $scope.checkRemotePermission(permissionData);
|
||||
} else {
|
||||
//FIREBASE HERE
|
||||
this.messagingService.requestPermission();
|
||||
// this.message = this.messagingService.currentMessage;
|
||||
}
|
||||
}
|
||||
|
||||
qrCodeClick() {
|
||||
@ -71,29 +61,14 @@ export class GuestCardComponent implements OnInit {
|
||||
this.cookiesService.logout();
|
||||
}
|
||||
|
||||
logout() {
|
||||
logout = () => {
|
||||
const bottomSheet = this._bottomSheet.open(ExitComponent);
|
||||
bottomSheet.afterDismissed().subscribe({
|
||||
next: (val) => {
|
||||
if (val) {
|
||||
this.deleteToken();
|
||||
this.router.navigate(['/login']);
|
||||
this.authService.logout();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getPurchases(
|
||||
start: Date | Moment = moment().subtract(1, 'months').startOf('month'),
|
||||
end: Date | Moment = moment()
|
||||
): Observable<any> {
|
||||
const token = this.cookiesService.getItem('token');
|
||||
const delta = moment(end).diff(moment(start), 'days');
|
||||
return this.wpJsonService.getTransactions(
|
||||
environment.systemId,
|
||||
token ?? '',
|
||||
environment.icardProxy
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,11 +1,7 @@
|
||||
<app-navbar
|
||||
*ngIf="phoneForm.value.phone && !isShowNumber"
|
||||
[title]="phoneForm.value.phone"
|
||||
(backEvent)="backToPhoneForm()"
|
||||
[ngStyle]="{
|
||||
position: 'absolute',
|
||||
top: 0
|
||||
}"
|
||||
[backEvent]="backToPhoneForm"
|
||||
></app-navbar>
|
||||
<h1>Участвуй в программе лояльности COFFEE LIKE</h1>
|
||||
<p class="description">Начни получать бонусы прямо сейчас</p>
|
||||
@ -38,10 +34,10 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<p class="offer">
|
||||
Используя приложение, вы принимаете условия в <span>соглашениях</span> и
|
||||
соглашаетесь на получение рекламно-информационных сообщений
|
||||
Используя приложение, вы принимаете <a href="https://docs.google.com/document/d/1tOZyI9DKerQpMY_N-hMExMB15j2F98E3VDqwIVMkk4I">условия</a>
|
||||
и соглашаетесь на получение рекламно-информационных сообщений
|
||||
</p>
|
||||
<button [disabled]="phoneForm.invalid">Принять участие</button>
|
||||
<button>Принять участие</button>
|
||||
</form>
|
||||
|
||||
<ng-template #smsCode>
|
||||
@ -55,7 +51,6 @@
|
||||
type="tel"
|
||||
placeholder="•"
|
||||
#field
|
||||
[appFocusNextInput]="inputFocusEmitter"
|
||||
formControlName="code"
|
||||
maxlength="1"
|
||||
/></label>
|
||||
@ -66,7 +61,6 @@
|
||||
type="tel"
|
||||
placeholder="•"
|
||||
#field1
|
||||
[appFocusNextInput]="inputFocusEmitter"
|
||||
formControlName="code1"
|
||||
maxlength="1"
|
||||
/></label>
|
||||
@ -77,7 +71,6 @@
|
||||
type="tel"
|
||||
placeholder="•"
|
||||
#field2
|
||||
[appFocusNextInput]="inputFocusEmitter"
|
||||
formControlName="code2"
|
||||
maxlength="1"
|
||||
/></label>
|
||||
@ -88,7 +81,6 @@
|
||||
type="tel"
|
||||
placeholder="•"
|
||||
#field3
|
||||
[appFocusNextInput]="inputFocusEmitter"
|
||||
formControlName="code3"
|
||||
maxlength="1"
|
||||
/></label>
|
||||
@ -97,10 +89,10 @@
|
||||
</form>
|
||||
<p class="resend-code">
|
||||
Не пришло SMS?<br />
|
||||
<ng-container *ngIf="timeLeft">
|
||||
Отправим повторно через {{timeLeft}}с
|
||||
<ng-container *ngIf="authService.timeLeft">
|
||||
Отправим повторно через {{authService.timeLeft}}с
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!timeLeft">
|
||||
<ng-container *ngIf="!authService.timeLeft">
|
||||
<span class="resend" (click)="submitNumber()">Отправить повторно</span>
|
||||
</ng-container>
|
||||
</p>
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
:host {
|
||||
padding-top: 48px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
max-width: 600px;
|
||||
margin: 0 auto 52px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
|
||||
h1 {
|
||||
margin-top: 20px;
|
||||
@ -47,13 +48,14 @@
|
||||
.offer {
|
||||
margin-top: 10px;
|
||||
padding: 0 16px;
|
||||
font-family: "Gowun Dodum";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 17px;
|
||||
text-align: center;
|
||||
span {
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--button-color);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,26 +1,24 @@
|
||||
import {
|
||||
AfterViewInit,
|
||||
Component,
|
||||
EventEmitter,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Router } from '@angular/router';
|
||||
import { AuthService } from 'src/app/services/auth.service';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { CookiesService } from 'src/app/services/cookies.service';
|
||||
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.scss'],
|
||||
})
|
||||
export class LoginComponent implements OnInit, AfterViewInit {
|
||||
export class LoginComponent implements OnInit {
|
||||
public isShowNumber: boolean = true;
|
||||
public phoneForm = new FormGroup({
|
||||
name: new FormControl('', []),
|
||||
name: new FormControl('', [Validators.required]),
|
||||
phone: new FormControl('', [Validators.required]),
|
||||
});
|
||||
public codeForm = new FormGroup({
|
||||
@ -30,40 +28,34 @@ export class LoginComponent implements OnInit, AfterViewInit {
|
||||
code3: new FormControl('', [Validators.required]),
|
||||
});
|
||||
private inputIds = ['field', 'field1', 'field2', 'field3'];
|
||||
timeLeft: number = 0;
|
||||
|
||||
constructor(
|
||||
private cookiesService: CookiesService,
|
||||
public authService: AuthService,
|
||||
private router: Router,
|
||||
private jsonrpc: JsonrpcService,
|
||||
private messageService: MessageService,
|
||||
private _snackBar: MatSnackBar,
|
||||
private jsonRpcService: JsonrpcService
|
||||
) { }
|
||||
|
||||
ngOnInit(): void { }
|
||||
|
||||
ngAfterViewInit() {
|
||||
setTimeout(() => {
|
||||
this.inputFocusEmitter.emit(`#${this.inputIds[0]}`);
|
||||
}, 1000);
|
||||
}
|
||||
@ViewChild('field', { static: false }) field!: ElementRef;
|
||||
@ViewChild('field1', { static: false }) field1!: ElementRef;
|
||||
@ViewChild('field2', { static: false }) field2!: ElementRef;
|
||||
@ViewChild('field3', { static: false }) field3!: ElementRef;
|
||||
|
||||
public inputFocusEmitter = new EventEmitter<string>();
|
||||
|
||||
@HostListener('window:keyup', ['$event'])
|
||||
@HostListener('window:input', ['$event'])
|
||||
HandlKeyEvents(event: any) {
|
||||
if (!event.target.classList.contains('field')) return;
|
||||
const key = event.key.toLocaleLowerCase();
|
||||
const key = event.data
|
||||
let elementId = '';
|
||||
|
||||
switch (key) {
|
||||
case 'backspace':
|
||||
case null:
|
||||
elementId = event.target.id;
|
||||
event.target.value = '';
|
||||
const prevInputIndex = this.inputIds.indexOf(elementId) - 1;
|
||||
if (prevInputIndex >= 0) {
|
||||
this.inputFocusEmitter.emit(`#${this.inputIds[prevInputIndex]}`);
|
||||
// this.inputFocusEmitter.emit(`#${this.inputIds[prevInputIndex]}`);
|
||||
this.focusFieldByIndex(prevInputIndex);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -71,118 +63,72 @@ export class LoginComponent implements OnInit, AfterViewInit {
|
||||
elementId = event.target.id;
|
||||
const index = this.inputIds.indexOf(elementId);
|
||||
const nextInputIndex = index + 1;
|
||||
if (event.target.value.length > 1) {
|
||||
event.target.value = event.target.value.slice(-1);
|
||||
}
|
||||
event.target.value = key;
|
||||
|
||||
if (nextInputIndex > 0 && nextInputIndex <= this.inputIds.length) {
|
||||
this.inputFocusEmitter.emit(`#${this.inputIds[nextInputIndex]}`);
|
||||
// this.inputFocusEmitter.emit(`#${this.inputIds[nextInputIndex]}`);
|
||||
this.focusFieldByIndex(nextInputIndex);
|
||||
}
|
||||
if (nextInputIndex == this.inputIds.length) {
|
||||
this.field3!.nativeElement.blur();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
focusFieldByIndex(i: number) {
|
||||
setTimeout(() => {
|
||||
switch (i) {
|
||||
case 0:
|
||||
this.field.nativeElement.focus();
|
||||
this.field.nativeElement.click();
|
||||
break;
|
||||
case 1:
|
||||
this.field1.nativeElement.focus();
|
||||
this.field1.nativeElement.click();
|
||||
break;
|
||||
case 2:
|
||||
this.field2.nativeElement.focus();
|
||||
this.field2.nativeElement.click();
|
||||
break;
|
||||
case 3:
|
||||
this.field3.nativeElement.focus();
|
||||
this.field3.nativeElement.click();
|
||||
break;
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
submitNumber() {
|
||||
const data = this.phoneForm.value;
|
||||
this.isShowNumber = false;
|
||||
if (this.timeLeft) {
|
||||
if (this.phoneForm.invalid) {
|
||||
this.phoneForm.markAsTouched();
|
||||
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'custom',
|
||||
summary: `Отправить повторно можно через ${this.timeLeft}с`,
|
||||
severity: 'error',
|
||||
summary: 'Введите имя и телефон',
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
this.jsonrpc
|
||||
.rpc(
|
||||
{
|
||||
method: 'sendVerifyByPhone',
|
||||
params: [data.phone],
|
||||
},
|
||||
RpcService.authService,
|
||||
false
|
||||
)
|
||||
.subscribe({
|
||||
next: (result) => {
|
||||
if (result.code !== 0) {
|
||||
this._snackBar.open('Произошла ошибка! Попробуйте позже', '', {
|
||||
duration: 3000,
|
||||
});
|
||||
}
|
||||
if (result.code === 0) {
|
||||
this.timeLeft = 60;
|
||||
const interval = setInterval(() => {
|
||||
if (this.timeLeft > 0) {
|
||||
this.timeLeft--;
|
||||
} else {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
const data = this.phoneForm.value;
|
||||
this.authService.sendVerifyByPhone(data.phone!);
|
||||
this.isShowNumber = false;
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error: ', error);
|
||||
},
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.inputFocusEmitter.emit(`#${this.inputIds[0]}`);
|
||||
this.field.nativeElement.focus();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
submitCode() {
|
||||
const data = this.codeForm.value;
|
||||
this.jsonrpc
|
||||
.rpc(
|
||||
{
|
||||
method: 'getTokenByPhone',
|
||||
params: [this.phoneForm.value.phone, Object.values(data).join('')],
|
||||
},
|
||||
RpcService.authService,
|
||||
false
|
||||
)
|
||||
.subscribe({
|
||||
next: (result) => {
|
||||
if (result.code === 0) {
|
||||
this.cookiesService.setCookie('token', result?.data?.token);
|
||||
this.jsonRpcService.rpc(
|
||||
{
|
||||
method: 'updateAdditionalInfo',
|
||||
params: [
|
||||
{
|
||||
first_name: this.phoneForm.value.name,
|
||||
birth_day: '01.01.1999'
|
||||
},
|
||||
],
|
||||
},
|
||||
RpcService.authService,
|
||||
true
|
||||
).subscribe({
|
||||
next: (value) => {
|
||||
this.router.navigate(['/'], {
|
||||
queryParams: {
|
||||
token: result?.data?.token,
|
||||
},
|
||||
});
|
||||
},
|
||||
error: (err) => {
|
||||
console.error(err);
|
||||
const phoneData = this.phoneForm.value;
|
||||
|
||||
}
|
||||
})
|
||||
// this.phoneConfirmed.emit(null);
|
||||
} else if (result.code === 230) {
|
||||
this._snackBar.open('Неверный код!', '', {
|
||||
duration: 3000,
|
||||
});
|
||||
// this.errorConfirmCode = true;
|
||||
}
|
||||
},
|
||||
error: (error) => {
|
||||
console.error(error);
|
||||
},
|
||||
});
|
||||
this.authService.submitCode(
|
||||
Object.values(data).join(''), phoneData.phone!, phoneData.name!);
|
||||
}
|
||||
|
||||
backToPhoneForm() {
|
||||
backToPhoneForm = () => {
|
||||
this.codeForm.setValue({
|
||||
code: '',
|
||||
code1: '',
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
<app-navbar title="Программа лояльности" (backEvent)="goBack()"></app-navbar>
|
||||
<app-navbar title="Программа лояльности" [backEvent]="goBack"></app-navbar>
|
||||
|
||||
<ng-container *ngIf="loyaltyProgram.currentLvlPeriod">
|
||||
<ng-container *ngIf="authService.currentLvlPeriod">
|
||||
<div class="loyality-program">
|
||||
<app-accordion header="Условия начисления бонусов">
|
||||
<p>
|
||||
Ваш текущий уровень {{ loyaltyProgram.currentLvl }},
|
||||
поэтому вам начисляется {{ loyaltyProgram.currentLvlPeriod.percent }}% от суммы покупки.
|
||||
Ваш текущий уровень {{ authService.userInfo?.current_level_and_cashback?.current_level ?? '--' }},
|
||||
поэтому вам начисляется {{ authService.currentLvlPeriod.percent }}% от суммы покупки.
|
||||
</p>
|
||||
<p>
|
||||
Смена уровня произойдет в начале следующего квартала,
|
||||
{{ loyaltyProgram.purchaseData.currentPeriod[1]
|
||||
{{ authService.currentPeriod[1]
|
||||
.locale("ru")
|
||||
.format("DD.MM.YY") }}.
|
||||
</p>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
.loyality-program {
|
||||
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@ -1,29 +1,23 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
import { Location } from '@angular/common';
|
||||
import { lvlPeriod } from 'src/app/interface/data';
|
||||
import { lvlPeriods } from 'src/app/app.constants';
|
||||
import { LoyaltyProgramService } from 'src/app/services/loyalty-program.service';
|
||||
import { AuthService } from 'src/app/services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'loyality-program',
|
||||
templateUrl: './loyality-program.component.html',
|
||||
styleUrls: ['./loyality-program.component.scss'],
|
||||
})
|
||||
export class LoyalityProgramComponent implements OnInit {
|
||||
export class LoyalityProgramComponent {
|
||||
constructor(
|
||||
private _location: Location,
|
||||
public loyaltyProgram: LoyaltyProgramService,
|
||||
public authService: AuthService,
|
||||
) { }
|
||||
|
||||
public lvlPeriods: lvlPeriod[] = lvlPeriods;
|
||||
|
||||
goBack() {
|
||||
this._location.back();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if(this.loyaltyProgram.currentLvlPeriod == null) {
|
||||
goBack = () => {
|
||||
this._location.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,14 +9,14 @@ import { MatIconModule } from '@angular/material/icon';
|
||||
import { GuestCardComponent } from './pages/guest-card/guest-card.component';
|
||||
import { QrCodeModule } from 'ng-qrcode';
|
||||
import { AccordionComponent } from './components/accordion/accordion.component';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatLegacyProgressSpinnerModule as MatProgressSpinnerModule } from '@angular/material/legacy-progress-spinner';
|
||||
import { LastOrderComponent } from './components/last-order/last-order.component';
|
||||
import { InviteFriendsComponent } from './components/invite-friends/invite-friends.component';
|
||||
import { LoginComponent } from './pages/login/login.component';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatLegacyFormFieldModule as MatFormFieldModule } from '@angular/material/legacy-form-field';
|
||||
import { NgxMatIntlTelInputComponent } from 'ngx-mat-intl-tel-input';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatLegacyInputModule as MatInputModule } from '@angular/material/legacy-input';
|
||||
import { DirectivesModule } from 'src/app/directives/directives.module';
|
||||
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
OnInit
|
||||
} from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatLegacySnackBar as MatSnackBar } from '@angular/material/legacy-snack-bar';
|
||||
import { Router } from '@angular/router';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { CookiesService } from 'src/app/services/cookies.service';
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, Inject } from '@angular/core';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { CookiesService } from './cookies.service';
|
||||
import { RpcService, JsonrpcService } from 'src/app/services/jsonrpc.service';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@ -9,6 +13,9 @@ export class AppleWalletService {
|
||||
private url: string = environment.appleWalletEndpoint;
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private cookiesService: CookiesService,
|
||||
private jsonrpc: JsonrpcService,
|
||||
@Inject(DOCUMENT) private document: Document,
|
||||
) { }
|
||||
|
||||
generateCard(token: string, user_id: string) {
|
||||
@ -32,4 +39,37 @@ export class AppleWalletService {
|
||||
}
|
||||
return this.http.post(`${this.url}/sendNotification/${user_id}`, body, options)
|
||||
}
|
||||
|
||||
|
||||
async addCardToWallet() {
|
||||
const token = this.cookiesService.getItem('token');
|
||||
try {
|
||||
const accountData = (
|
||||
await lastValueFrom(
|
||||
this.jsonrpc.rpc(
|
||||
{
|
||||
method: 'getTokenData',
|
||||
params: [],
|
||||
},
|
||||
RpcService.authService,
|
||||
true
|
||||
)
|
||||
)
|
||||
).data;
|
||||
|
||||
if (token && accountData.user_id) {
|
||||
this.generateCard(token, accountData.user_id).subscribe({
|
||||
next: (res: any) => {
|
||||
this.document.location.href = res.url;
|
||||
},
|
||||
error: (err) => {
|
||||
console.log('Error: ', err);
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
243
angular/src/app/services/auth.service.ts
Normal file
243
angular/src/app/services/auth.service.ts
Normal file
@ -0,0 +1,243 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CookiesService } from './cookies.service';
|
||||
import { WpJsonService } from './wp-json.service';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { JsonrpcService, RpcService } from './jsonrpc.service';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { UserInfo, lvlPeriod, UserInfoWalletBalance, Moment } from '../interface/data';
|
||||
import { lvlPeriods } from 'src/app/app.constants';
|
||||
import moment from 'moment-timezone';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthService {
|
||||
lastPeriod: Moment[] = [];
|
||||
currentPeriod: Moment[] = [];
|
||||
userInfo?: UserInfo;
|
||||
loading: boolean = false;
|
||||
error: any;
|
||||
timeLeft: number = 0;
|
||||
|
||||
get currentLvlPeriod(): lvlPeriod {
|
||||
return lvlPeriods[
|
||||
this.userInfo?.current_level_and_cashback ? this.userInfo?.current_level_and_cashback.current_level - 1 : 0];
|
||||
}
|
||||
|
||||
constructor(
|
||||
private cookiesService: CookiesService,
|
||||
private wpJsonService: WpJsonService,
|
||||
private jsonrpc: JsonrpcService,
|
||||
private messageService: MessageService,
|
||||
private router: Router,
|
||||
) {
|
||||
this.getCurrentQuarterOfYear();
|
||||
}
|
||||
|
||||
get token(): string | undefined {
|
||||
return this.cookiesService.getItem('token');
|
||||
}
|
||||
|
||||
get authorized(): boolean {
|
||||
return !!this.token;
|
||||
}
|
||||
|
||||
getUserInfo() {
|
||||
const token = this.cookiesService.getItem('token');
|
||||
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
|
||||
this.wpJsonService
|
||||
.getCustomerInfo(
|
||||
environment.systemId,
|
||||
token,
|
||||
environment.icardProxy,
|
||||
)
|
||||
.subscribe({
|
||||
next: (value) => {
|
||||
if (value && value.error && value.error.code === 11) {
|
||||
this.wpJsonService.newCustomer(
|
||||
environment.systemId,
|
||||
token,
|
||||
environment.icardProxy,
|
||||
)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.getUserInfo();
|
||||
}
|
||||
})
|
||||
} else if (value && value.error && value.error.code > 1) {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: 'Произошла ошибка! Попробуйте позже',
|
||||
});
|
||||
} else if (value && value.customer_info) {
|
||||
this.userInfo = value.customer_info;
|
||||
|
||||
this.cookiesService.setCookie('phone-number', this.userInfo!.phone?.slice(2));
|
||||
}
|
||||
},
|
||||
error: (e) => {
|
||||
this.error = e;
|
||||
},
|
||||
complete: () => {
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.userInfo = undefined;
|
||||
this.cookiesService.logout();
|
||||
this.router.navigate(['/login']);
|
||||
}
|
||||
|
||||
sendVerifyByPhone(phone: string) {
|
||||
if (this.timeLeft) {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'custom',
|
||||
summary: `Отправить повторно можно через ${this.timeLeft}с`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.jsonrpc
|
||||
.rpc(
|
||||
{
|
||||
method: 'sendVerifyByPhone',
|
||||
params: [phone],
|
||||
},
|
||||
RpcService.authService,
|
||||
false
|
||||
)
|
||||
.subscribe({
|
||||
next: (result) => {
|
||||
if (result.code !== 0) {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: 'Произошла ошибка! Попробуйте позже',
|
||||
});
|
||||
}
|
||||
if (result.code === 0) {
|
||||
this.timeLeft = 60;
|
||||
const interval = setInterval(() => {
|
||||
if (this.timeLeft > 0) {
|
||||
this.timeLeft--;
|
||||
} else {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error: ', error);
|
||||
},
|
||||
complete: () => {
|
||||
this.loading = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
submitCode(code: string, phone: string, name: string) {
|
||||
this.loading = true;
|
||||
this.jsonrpc
|
||||
.rpc(
|
||||
{
|
||||
method: 'getTokenByPhone',
|
||||
params: [phone, code],
|
||||
},
|
||||
RpcService.authService,
|
||||
false
|
||||
)
|
||||
.subscribe({
|
||||
next: (result) => {
|
||||
if (result.code === 0) {
|
||||
this.cookiesService.setCookie('token', result?.data?.token);
|
||||
this.jsonrpc.rpc(
|
||||
{
|
||||
method: 'updateAdditionalInfo',
|
||||
params: [
|
||||
{
|
||||
first_name: name,
|
||||
birth_day: '01.01.1999'
|
||||
},
|
||||
],
|
||||
},
|
||||
RpcService.authService,
|
||||
true
|
||||
).subscribe({
|
||||
next: () => {
|
||||
this.router.navigate(['/']);
|
||||
this.getUserInfo();
|
||||
},
|
||||
error: (err) => {
|
||||
console.error(err);
|
||||
},
|
||||
})
|
||||
} else if (result.code === 230) {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: 'Неверный код!',
|
||||
});
|
||||
}
|
||||
},
|
||||
error: (error) => {
|
||||
console.error(error);
|
||||
},
|
||||
complete: () => {
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getCurrentQuarterOfYear() {
|
||||
const quarters = [
|
||||
[
|
||||
moment().subtract(1, 'years').endOf('year').subtract(3, 'months'),
|
||||
moment().startOf('year').add(10, 'days'),
|
||||
],
|
||||
[
|
||||
moment().startOf('year').add(10, 'days'),
|
||||
moment().startOf('year').add(3, 'months'),
|
||||
],
|
||||
[
|
||||
moment().startOf('year').add(3, 'months'),
|
||||
moment().startOf('year').add(6, 'months'),
|
||||
],
|
||||
[
|
||||
moment().startOf('year').add(6, 'months'),
|
||||
moment().startOf('year').add(9, 'months'),
|
||||
],
|
||||
[
|
||||
moment().startOf('year').add(9, 'months'),
|
||||
moment().startOf('year').add(12, 'months'),
|
||||
],
|
||||
];
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (moment().isBetween(quarters[i][0], quarters[i][1])) {
|
||||
this.lastPeriod = quarters[i - 1];
|
||||
this.currentPeriod = quarters[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getBalanceAmount(loyaltyPrograms: UserInfoWalletBalance[]) {
|
||||
return (loyaltyPrograms || []).reduce((accumulator, currentValue) => {
|
||||
if (currentValue.wallet.name !== 'Федеральная программа лояльности') {
|
||||
return accumulator
|
||||
}
|
||||
return accumulator + currentValue.balance;
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
@ -1,173 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import moment from 'moment';
|
||||
import { Moment, Purchase, lvlPeriod } from '../interface/data';
|
||||
import { lvlPeriods } from '../app.constants';
|
||||
|
||||
export interface IPurchaseData {
|
||||
currentPeriod: Moment[];
|
||||
lastPeriod: Moment[];
|
||||
lastPurchases: Purchase[];
|
||||
currentPurchases: Purchase[];
|
||||
lastAmount?: number;
|
||||
currentAmount?: number;
|
||||
$loading: boolean;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LoyaltyProgramService {
|
||||
public purchaseData: IPurchaseData = {
|
||||
currentPeriod: [],
|
||||
lastPeriod: [],
|
||||
lastPurchases: [],
|
||||
currentPurchases: [],
|
||||
$loading: false,
|
||||
get currentAmount(): number {
|
||||
const amount = this.currentPurchases.reduce(
|
||||
(accumulator, currentValue) => {
|
||||
if (
|
||||
['CancelPayFromWallet', 'CancelRefillWalletFromOrder'].includes(
|
||||
currentValue.transactionType || ''
|
||||
)
|
||||
) {
|
||||
return accumulator - currentValue.orderSum!;
|
||||
} else if (
|
||||
['PayFromWallet', 'RefillWalletFromOrder'].includes(
|
||||
currentValue.transactionType || ''
|
||||
)
|
||||
) {
|
||||
return accumulator + currentValue.orderSum!;
|
||||
}
|
||||
return accumulator;
|
||||
},
|
||||
0
|
||||
);
|
||||
|
||||
return amount * 1;
|
||||
},
|
||||
get lastAmount(): number {
|
||||
const amount = this.lastPurchases.reduce((accumulator, currentValue) => {
|
||||
if (
|
||||
['CancelPayFromWallet', 'CancelRefillWalletFromOrder'].includes(
|
||||
currentValue.transactionType || ''
|
||||
)
|
||||
) {
|
||||
return accumulator - currentValue.orderSum!;
|
||||
} else if (
|
||||
['PayFromWallet', 'RefillWalletFromOrder'].includes(
|
||||
currentValue.transactionType || ''
|
||||
)
|
||||
) {
|
||||
return accumulator + currentValue.orderSum!;
|
||||
}
|
||||
return accumulator;
|
||||
}, 0);
|
||||
return amount * 1;
|
||||
},
|
||||
};
|
||||
|
||||
public currentLvl: number = 1;
|
||||
public currentLvlPeriod!: lvlPeriod;
|
||||
public lvlPeriods: lvlPeriod[] = lvlPeriods;
|
||||
|
||||
constructor() {
|
||||
this.getCurrentQuarterOfYear();
|
||||
}
|
||||
|
||||
getCurrentQuarterOfYear() {
|
||||
const quarters = [
|
||||
[
|
||||
moment().subtract(1, 'years').endOf('year').subtract(3, 'months'),
|
||||
moment().startOf('year').add(10, 'days'),
|
||||
],
|
||||
[
|
||||
moment().startOf('year').add(10, 'days'),
|
||||
moment().startOf('year').add(3, 'months'),
|
||||
],
|
||||
[
|
||||
moment().startOf('year').add(3, 'months'),
|
||||
moment().startOf('year').add(6, 'months'),
|
||||
],
|
||||
[
|
||||
moment().startOf('year').add(6, 'months'),
|
||||
moment().startOf('year').add(9, 'months'),
|
||||
],
|
||||
[
|
||||
moment().startOf('year').add(9, 'months'),
|
||||
moment().startOf('year').add(12, 'months'),
|
||||
],
|
||||
];
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (moment().isBetween(quarters[i][0], quarters[i][1])) {
|
||||
this.purchaseData.lastPeriod = quarters[i - 1];
|
||||
this.purchaseData.currentPeriod = quarters[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getBalanceAmount(loyaltyPrograms: any[]) {
|
||||
return (loyaltyPrograms || []).reduce((accumulator, currentValue) => {
|
||||
if (currentValue.wallet.name !== 'Федеральная программа лояльности') {
|
||||
return accumulator
|
||||
}
|
||||
return accumulator + currentValue.balance;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
setLastPurchases(purchases: Purchase[]) {
|
||||
this.purchaseData.lastPurchases = (purchases || []).filter((value: Purchase) => {
|
||||
return moment(value.transactionCreateDate).isBetween(
|
||||
this.purchaseData.lastPeriod[0],
|
||||
this.purchaseData.lastPeriod[1]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
setCurrentPurchases(purchases: Purchase[]) {
|
||||
this.purchaseData.currentPurchases = (purchases || []).filter((value: Purchase) => {
|
||||
return moment(value.transactionCreateDate).isBetween(
|
||||
this.purchaseData.currentPeriod[0],
|
||||
this.purchaseData.currentPeriod[1]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
filterPurchases(purchases: Purchase[]) {
|
||||
return (purchases || []).filter((purchase: Purchase) =>
|
||||
[
|
||||
'PayFromWallet',
|
||||
'RefillWalletFromOrder',
|
||||
'CancelPayFromWallet',
|
||||
'CancelRefillWalletFromOrder',
|
||||
].includes(purchase.transactionType || '')
|
||||
);
|
||||
}
|
||||
|
||||
getLastPurchase(purchases: Purchase[]) {
|
||||
return purchases.filter((purchase: Purchase) =>
|
||||
['PayFromWallet', 'RefillWalletFromOrder'].includes(
|
||||
purchase.transactionType || ''
|
||||
)
|
||||
)[0];
|
||||
}
|
||||
|
||||
setCurrentLvl(currentAmount: number) {
|
||||
const index = this.lvlPeriods.findIndex(
|
||||
(item) =>
|
||||
item.start <= currentAmount && currentAmount <= (item.end || Infinity)
|
||||
)!;
|
||||
if (index != -1) {
|
||||
this.currentLvlPeriod = this.lvlPeriods[index];
|
||||
this.currentLvl = index + 1;
|
||||
}
|
||||
}
|
||||
|
||||
getNextLevel(): lvlPeriod {
|
||||
if(this.currentLvl == this.lvlPeriods.length) {
|
||||
return lvlPeriods[this.currentLvl - 1];
|
||||
}
|
||||
return lvlPeriods[this.currentLvl];
|
||||
}
|
||||
}
|
||||
@ -50,6 +50,7 @@ export class MessagingService {
|
||||
)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'custom',
|
||||
summary: 'Спасибо за подписку!',
|
||||
@ -57,6 +58,7 @@ export class MessagingService {
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Error: ', err);
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: 'Произошла ошибка, попробуйте позже',
|
||||
@ -65,11 +67,20 @@ export class MessagingService {
|
||||
});
|
||||
}
|
||||
|
||||
checkRequestPermission() {
|
||||
return Notification.permission !== 'granted' ? false : true;
|
||||
}
|
||||
|
||||
requestPermission() {
|
||||
if (this.checkRequestPermission()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.angularFireMessaging.requestToken.subscribe({
|
||||
next: (token) => {
|
||||
this.updateToken(token);
|
||||
this.receiveMessage();
|
||||
},
|
||||
error: (e) => console.error(e),
|
||||
});
|
||||
|
||||
@ -5,8 +5,6 @@ import {CookiesService} from "./cookies.service";
|
||||
import { Observable, of, switchMap } from "rxjs";
|
||||
import { JsonRpcBody } from "./jsonrpc.service";
|
||||
import { DeliveryType, AcceptedOrder, Product } from "../interface/data";
|
||||
import {ActivatedRoute} from "@angular/router";
|
||||
import {Order} from "../models/order";
|
||||
|
||||
export enum Method {
|
||||
|
||||
@ -23,7 +21,6 @@ export class WpJsonService {
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private cookiesService: CookiesService,
|
||||
private route: ActivatedRoute,
|
||||
) { }
|
||||
|
||||
getDeliveryTypes(): Observable<DeliveryType[]> {
|
||||
@ -43,18 +40,11 @@ export class WpJsonService {
|
||||
}
|
||||
|
||||
getCustomerInfo(systemId: string, token: string, url: string): Observable<any> {
|
||||
return this._request(`customer_info/${systemId}/${token}/`, 'GET', null, false, url).pipe(
|
||||
switchMap((response) => {
|
||||
// TODO: typescript compile optional chaining ('response?.customer_info?.errorCode') without check ('response.customer_info.errorCode')
|
||||
if (response && response.customer_info && response.customer_info.errorCode !== 'Customer_CustomerNotFound') {
|
||||
return of(response)
|
||||
} else {
|
||||
return this.newCustomer(systemId, token, url).pipe(
|
||||
switchMap(() => this.getCustomerInfo(systemId, token, url))
|
||||
)
|
||||
return this._request(`customer_info/${systemId}/${token}/`, 'GET', null, false, url);
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
getLastPurchase(systemId: string, token: string): Observable<any> {
|
||||
return this._request(`last_trans/${systemId}/${token}/`, 'GET', null, false, environment.icardProxy);
|
||||
}
|
||||
|
||||
newCustomer(systemId: string, token: string, url: string): Observable<any> {
|
||||
|
||||
24
angular/src/app/utils.ts
Normal file
24
angular/src/app/utils.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { environment } from "src/environments/environment.prod";
|
||||
|
||||
export enum DeviceType {
|
||||
ios,
|
||||
android,
|
||||
}
|
||||
|
||||
export function getTypeDevice(): DeviceType {
|
||||
const userAgent = window.navigator.userAgent.toLowerCase();
|
||||
const ios = /iphone|ipod|ipad/.test(userAgent);
|
||||
return ios ? DeviceType.ios : DeviceType.android;
|
||||
}
|
||||
|
||||
export async function pwaInstalled(): Promise<boolean> {
|
||||
if ("getInstalledRelatedApps" in navigator) {
|
||||
const apps = await (window.navigator as any).getInstalledRelatedApps();
|
||||
for (const app of apps) {
|
||||
if (app.url == environment.manifestUrl) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
BIN
angular/src/assets/970х250_3.png
Normal file
BIN
angular/src/assets/970х250_3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 291 KiB |
232
angular/src/assets/apple_wallet.svg
Normal file
232
angular/src/assets/apple_wallet.svg
Normal file
@ -0,0 +1,232 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="750"
|
||||
height="563.15234"
|
||||
viewBox="0 0 198.43748 149.00072"
|
||||
version="1.1"
|
||||
id="svg5668"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
sodipodi:docname="applewallet.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs5662">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4120">
|
||||
<stop
|
||||
style="stop-color:#cbc9be;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4116" />
|
||||
<stop
|
||||
style="stop-color:#c8c5bb;stop-opacity:0"
|
||||
offset="1"
|
||||
id="stop4118" />
|
||||
</linearGradient>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath3753">
|
||||
<path
|
||||
sodipodi:nodetypes="sscsscsss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3755"
|
||||
d="m -31.594972,54.69743 c -10.576039,0 -13.725777,6.338221 -13.725777,14.211535 V 85.50908 c 0,-7.873315 3.149738,-14.211536 13.725777,-14.211536 H 122.86692 c 10.57602,0 13.72577,6.338221 13.72577,14.211536 V 68.908965 c 0,-7.873314 -3.14975,-14.211535 -13.72577,-14.211535 z"
|
||||
style="opacity:1;fill:#4095ca;fill-opacity:1;stroke:none;stroke-width:18;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.607477" />
|
||||
</clipPath>
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter3829"
|
||||
x="-0.047008887"
|
||||
width="1.0940178"
|
||||
y="-0.27754272"
|
||||
height="1.5550854">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="3.5631452"
|
||||
id="feGaussianBlur3831" />
|
||||
</filter>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath3929">
|
||||
<path
|
||||
style="opacity:1;fill:#f16d5e;fill-opacity:1;stroke:none;stroke-width:18;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.607477"
|
||||
d="m 83.818361,394.03231 c -39.972431,0 -51.876954,23.95549 -51.876954,53.71292 v 7.60641 l 176.408203,0.32617 c 47.12943,0.0873 66.42745,19.0478 86.06641,45.57812 5.00155,6.75662 11.08589,15.9143 17.02733,21.86135 17.74131,17.75789 41.00574,26.62878 64.27148,26.62889 23.26576,1.1e-4 46.53019,-8.87097 64.2715,-26.62889 5.94144,-5.94701 12.02578,-15.10473 17.02735,-21.86135 19.63895,-26.53032 38.93696,-45.49096 86.0664,-45.57812 l 176.40819,-0.32617 v -7.60641 c 0,-29.75743 -11.90453,-53.71292 -51.87692,-53.71292 z"
|
||||
id="path3931"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sscssssssscsss" />
|
||||
</clipPath>
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter4089"
|
||||
x="-0.053576618"
|
||||
width="1.1071532"
|
||||
y="-0.071352741"
|
||||
height="1.1427055">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="16.742692"
|
||||
id="feGaussianBlur4091" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4120"
|
||||
id="linearGradient4122"
|
||||
x1="-53.582775"
|
||||
y1="120.72184"
|
||||
x2="-53.582775"
|
||||
y2="114.41766"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4120"
|
||||
id="linearGradient4131"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-53.582775"
|
||||
y1="120.72184"
|
||||
x2="-53.582775"
|
||||
y2="114.41766"
|
||||
gradientTransform="translate(190.17547)" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.35"
|
||||
inkscape:cx="-135.71429"
|
||||
inkscape:cy="572.85714"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="g4145"
|
||||
showgrid="false"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
showguides="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1620"
|
||||
inkscape:window-height="1010"
|
||||
inkscape:window-x="-6"
|
||||
inkscape:window-y="-6"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:guide-bbox="true"
|
||||
units="px"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="true"
|
||||
inkscape:deskcolor="#d1d1d1" />
|
||||
<metadata
|
||||
id="metadata5665">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(53.582777,-46.342901)">
|
||||
<g
|
||||
id="g5740"
|
||||
transform="matrix(1,0,0,1.0056496,24.568462,4.937821)"
|
||||
style="fill:#ff0000;fill-opacity:0;stroke-width:0.997187">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="rect5713"
|
||||
d="m 10.108037,0.24338852 c -36.91084,0 -63.87995,22.39413848 -63.87995,64.79580448 V 198.5445 c 0,42.40171 26.96911,64.79582 63.87995,64.79582 H 146.93146 c 36.91084,0 63.87996,-22.39411 63.87996,-64.79582 V 65.039193 c 0,-42.401691 -26.96912,-64.79580448 -63.87996,-64.79580448 z"
|
||||
style="opacity:1;fill:#ff0000;fill-opacity:0;stroke:none;stroke-width:2.09409;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.383178" />
|
||||
</g>
|
||||
<g
|
||||
id="g4145">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4095"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,-53.771913,0.24338852)"
|
||||
d="m 77.738281,174.23438 c -50.500035,-2e-5 -77.02343725,30.81653 -77.02343725,77.02343 v 409.10547 c 0,46.20688 26.52340225,77.02344 77.02343725,77.02344 H 673.69141 c 50.50004,0 77.02343,-30.81656 77.02343,-77.02344 V 251.25781 c 0,-46.2069 -26.52339,-77.02344 -77.02343,-77.02343 z"
|
||||
style="opacity:1;fill:#d9d6cc;fill-opacity:1;stroke:none;stroke-width:155.717;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
sodipodi:nodetypes="sscssssssscsss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="rect3638"
|
||||
d="m -31.594972,54.697449 c -10.576039,0 -13.725777,6.338221 -13.725777,14.211536 v 51.812855 l 46.6746696,0.0863 c 12.4696614,0.0231 17.5755964,5.03973 22.7717374,12.05921 1.323329,1.78769 2.933142,4.21066 4.50515,5.78415 4.694053,4.69844 10.849433,7.04553 17.005162,7.04556 6.15573,3e-5 12.31111,-2.34711 17.005165,-7.04556 1.572006,-1.57348 3.181821,-3.99646 4.505151,-5.78415 5.196141,-7.01948 10.302073,-12.03615 22.771737,-12.05921 l 46.674667,-0.0863 V 68.908985 c 0,-7.873315 -3.14974,-14.211536 -13.72577,-14.211536 z"
|
||||
style="opacity:1;fill:#f16d5e;fill-opacity:1;stroke:none;stroke-width:18;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.607477" />
|
||||
<path
|
||||
transform="matrix(0.26458333,0,0,0.26458333,-53.771913,0.24338852)"
|
||||
sodipodi:nodetypes="sscsscsss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3676"
|
||||
d="m 83.818359,205.81055 c -39.97243,0 -51.876953,23.95548 -51.876953,53.71289 v 62.74059 c 0,-29.75741 11.904523,-53.71289 51.876953,-53.71289 H 667.61133 c 39.97239,0 51.87695,23.95548 51.87695,53.71289 v -62.74059 c 0,-29.75741 -11.90456,-53.71289 -51.87695,-53.71289 z"
|
||||
style="opacity:1;fill:#3295c9;fill-opacity:1;stroke:none;stroke-width:68.0315;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.607477" />
|
||||
<path
|
||||
clip-path="url(#clipPath3753)"
|
||||
sodipodi:nodetypes="sscsscsss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3685"
|
||||
d="m -31.594972,71.297543 c -10.576039,0 -13.725777,6.338221 -13.725777,14.211536 v 16.600111 c 0,-7.873312 3.149738,-14.211532 13.725777,-14.211532 H 122.86692 c 10.57602,0 13.72577,6.33822 13.72577,14.211532 V 85.509079 c 0,-7.873315 -3.14975,-14.211536 -13.72577,-14.211536 z"
|
||||
style="opacity:1;fill:#000000;fill-opacity:0.317757;stroke:none;stroke-width:18;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.607477;filter:url(#filter3829)" />
|
||||
<path
|
||||
style="opacity:1;fill:#fcad00;fill-opacity:1;stroke:none;stroke-width:18;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.607477"
|
||||
d="m -31.594972,71.297544 c -10.576039,0 -13.725777,6.338221 -13.725777,14.211535 v 16.600111 c 0,-7.873311 3.149738,-14.211532 13.725777,-14.211532 H 122.86692 c 10.57602,0 13.72577,6.338221 13.72577,14.211532 V 85.509079 c 0,-7.873314 -3.14975,-14.211535 -13.72577,-14.211535 z"
|
||||
id="path3833"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sscsscsss" />
|
||||
<path
|
||||
transform="translate(4.6064453e-7,16.600115)"
|
||||
style="opacity:1;fill:#000000;fill-opacity:0.313725;stroke:none;stroke-width:18;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.607477;filter:url(#filter3829)"
|
||||
d="m -31.594972,71.297543 c -10.576039,0 -13.725777,6.338221 -13.725777,14.211536 v 16.600111 c 0,-7.873312 3.149738,-14.211532 13.725777,-14.211532 H 122.86692 c 10.57602,0 13.72577,6.33822 13.72577,14.211532 V 85.509079 c 0,-7.873315 -3.14975,-14.211536 -13.72577,-14.211536 z"
|
||||
id="path3835"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sscsscsss"
|
||||
clip-path="url(#clipPath3753)" />
|
||||
<path
|
||||
sodipodi:nodetypes="sscsscsss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3837"
|
||||
d="m -31.594972,87.897659 c -10.576039,0 -13.725777,6.338221 -13.725777,14.211531 v 16.60012 c 0,-7.87332 3.149738,-14.21154 13.725777,-14.21154 H 122.86692 c 10.57602,0 13.72577,6.33822 13.72577,14.21154 v -16.60012 c 0,-7.87331 -3.14975,-14.211531 -13.72577,-14.211531 z"
|
||||
style="opacity:1;fill:#50be3d;fill-opacity:1;stroke:none;stroke-width:18;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.607477" />
|
||||
<path
|
||||
clip-path="url(#clipPath3753)"
|
||||
sodipodi:nodetypes="sscsscsss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3839"
|
||||
d="m -31.594972,71.297543 c -10.576039,0 -13.725777,6.338221 -13.725777,14.211536 v 16.600111 c 0,-7.873312 3.149738,-14.211532 13.725777,-14.211532 H 122.86692 c 10.57602,0 13.72577,6.33822 13.72577,14.211532 V 85.509079 c 0,-7.873315 -3.14975,-14.211536 -13.72577,-14.211536 z"
|
||||
style="opacity:1;fill:#000000;fill-opacity:0.313725;stroke:none;stroke-width:18;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.607477;filter:url(#filter3829)"
|
||||
transform="translate(9.2128905e-7,33.20023)" />
|
||||
<path
|
||||
clip-path="url(#clipPath3929)"
|
||||
style="opacity:1;fill:#f26d5f;fill-opacity:0.29803923;stroke:none;stroke-width:155.717;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4089)"
|
||||
d="m 77.738281,174.23438 c -50.500036,-2e-5 -77.02343725,30.81653 -77.02343725,77.02343 v 409.10547 c 0,46.20688 26.52340125,77.02344 77.02343725,77.02344 H 673.69141 c 50.50004,0 77.02343,-30.81656 77.02343,-77.02344 V 251.25781 c 0,-46.2069 -26.52339,-77.02344 -77.02343,-77.02343 z m 6.080078,31.57617 H 667.61133 c 39.97239,0 51.87695,23.95548 51.87695,53.71289 v 195.82812 l -176.4082,0.32617 c -47.12944,0.0872 -66.42745,19.04781 -86.06641,45.57813 -5.00156,6.75662 -11.0859,15.91432 -17.02734,21.86133 -17.74131,17.75792 -41.00574,26.62902 -64.27149,26.6289 -23.26574,-1.1e-4 -46.53018,-8.87102 -64.27148,-26.6289 -5.94145,-5.94705 -12.02579,-15.10471 -17.02734,-21.86133 -19.63896,-26.53032 -38.93698,-45.49082 -86.06641,-45.57813 L 31.941406,455.35156 V 259.52344 c 0,-29.75741 11.904523,-53.71289 51.876953,-53.71289 z"
|
||||
id="path3848"
|
||||
inkscape:connector-curvature="0"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,-53.771913,0.24338852)" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="rect4114"
|
||||
d="m -53.582775,108.55904 h 8.262028 v 12.1628 h -8.262028 z"
|
||||
style="opacity:1;fill:url(#linearGradient4122);fill-opacity:1;stroke:none;stroke-width:18;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.607477" />
|
||||
<path
|
||||
style="opacity:1;fill:url(#linearGradient4131);fill-opacity:1;stroke:none;stroke-width:18;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.607477"
|
||||
d="m 136.59269,108.55904 h 8.26203 v 12.1628 h -8.26203 z"
|
||||
id="path4129"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
@ -8,19 +8,18 @@ export const environment = {
|
||||
hasBonusProgram: true,
|
||||
systemId: 'tsQ2cu59Xz9qgGTm3z',
|
||||
defaultUrl: 'https://club.coffee-like.com',
|
||||
manifestUrl: 'https://club.coffee-like.com/manifest.webmanifest',
|
||||
firebase: {
|
||||
apiKey: "AIzaSyCnKvln5itnrBj62POCPHxshAN_Vmd0zds",
|
||||
authDomain: "fashionlogicanotification.firebaseapp.com",
|
||||
projectId: "fashionlogicanotification",
|
||||
storageBucket: "fashionlogicanotification.appspot.com",
|
||||
messagingSenderId: "99855572145",
|
||||
|
||||
appId: "1:99855572145:web:7548c189d61b3bcc92d690",
|
||||
measurementId: "G-RQF97ZK7R1"
|
||||
apiKey: "AIzaSyDTb_xuMz2vDx8xGs34AJiltraKVlwmrtY",
|
||||
authDomain: "coffee-like-77bfe.firebaseapp.com",
|
||||
projectId: "coffee-like-77bfe",
|
||||
storageBucket: "coffee-like-77bfe.appspot.com",
|
||||
messagingSenderId: "1094726277369",
|
||||
appId: "1:1094726277369:web:8af560662da7700e7a2a28"
|
||||
},
|
||||
version: packageJson.version,
|
||||
appleWalletEndpoint: 'https://apple-push-notifications.it-retail.tech/apns/api',
|
||||
appleWalletEndpoint: 'https://apple-wallet-iiko.it-retail.tech/apns/api',
|
||||
icardProxy: 'https://club.coffee-like.com/api/icard-proxy/',
|
||||
appleWalletSecret: 'Token F5mbzEERAznGKVbB6l',
|
||||
clientName: 'coffeeLike'
|
||||
clientName: 'coffeelike'
|
||||
}
|
||||
|
||||
@ -8,18 +8,18 @@ export const environment = {
|
||||
hasBonusProgram: true,
|
||||
systemId: 'tsQ2cu59Xz9qgGTm3z',
|
||||
defaultUrl: 'http://192.168.0.179:4200',
|
||||
manifestUrl: 'https://club.coffee-like.com/manifest.webmanifest',
|
||||
firebase: {
|
||||
apiKey: 'AIzaSyCnKvln5itnrBj62POCPHxshAN_Vmd0zds',
|
||||
authDomain: 'fashionlogicanotification.firebaseapp.com',
|
||||
projectId: 'fashionlogicanotification',
|
||||
storageBucket: 'fashionlogicanotification.appspot.com',
|
||||
messagingSenderId: '99855572145',
|
||||
appId: '1:99855572145:web:7548c189d61b3bcc92d690',
|
||||
measurementId: 'G-RQF97ZK7R1',
|
||||
apiKey: "AIzaSyDTb_xuMz2vDx8xGs34AJiltraKVlwmrtY",
|
||||
authDomain: "coffee-like-77bfe.firebaseapp.com",
|
||||
projectId: "coffee-like-77bfe",
|
||||
storageBucket: "coffee-like-77bfe.appspot.com",
|
||||
messagingSenderId: "1094726277369",
|
||||
appId: "1:1094726277369:web:8af560662da7700e7a2a28"
|
||||
},
|
||||
version: packageJson.version,
|
||||
appleWalletEndpoint: 'http://192.168.0.179:4200/apns/api',
|
||||
icardProxy: 'http://192.168.0.14:4200/icard-proxy/',
|
||||
appleWalletEndpoint: 'https://apple-wallet-iiko.it-retail.tech/apns/api',
|
||||
icardProxy: 'https://club.coffee-like.com/api/icard-proxy/',
|
||||
appleWalletSecret: 'Token F5mbzEERAznGKVbB6l',
|
||||
clientName: 'coffeeLike'
|
||||
clientName: 'coffeelike'
|
||||
};
|
||||
|
||||
13
angular/src/firebase-messaging-sw.js
vendored
13
angular/src/firebase-messaging-sw.js
vendored
@ -2,13 +2,12 @@ importScripts('https://www.gstatic.com/firebasejs/3.6.9/firebase-app.js');
|
||||
importScripts('https://www.gstatic.com/firebasejs/3.6.9/firebase-messaging.js');
|
||||
|
||||
firebase.initializeApp({
|
||||
apiKey: "AIzaSyCnKvln5itnrBj62POCPHxshAN_Vmd0zds",
|
||||
authDomain: "fashionlogicanotification.firebaseapp.com",
|
||||
projectId: "fashionlogicanotification",
|
||||
storageBucket: "fashionlogicanotification.appspot.com",
|
||||
messagingSenderId: "99855572145",
|
||||
appId: "1:99855572145:web:7548c189d61b3bcc92d690",
|
||||
measurementId: "G-RQF97ZK7R1"
|
||||
apiKey: "AIzaSyDTb_xuMz2vDx8xGs34AJiltraKVlwmrtY",
|
||||
authDomain: "coffee-like-77bfe.firebaseapp.com",
|
||||
projectId: "coffee-like-77bfe",
|
||||
storageBucket: "coffee-like-77bfe.appspot.com",
|
||||
messagingSenderId: "1094726277369",
|
||||
appId: "1:1094726277369:web:8af560662da7700e7a2a28"
|
||||
});
|
||||
|
||||
const messaging = firebase.messaging();
|
||||
@ -50,5 +50,9 @@
|
||||
"purpose": "maskable any"
|
||||
}
|
||||
],
|
||||
"gcm_sender_id": "99855572145"
|
||||
"gcm_sender_id": "99855572145",
|
||||
"related_applications": [{
|
||||
"platform": "webapp",
|
||||
"url": "https://club.coffee-like.com/manifest.webmanifest"
|
||||
}]
|
||||
}
|
||||
|
||||
@ -1,5 +1,89 @@
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
@include mat.core();
|
||||
|
||||
$primary: (
|
||||
50 : #f0e4ec,
|
||||
100 : #d9bcd0,
|
||||
200 : #bf90b0,
|
||||
300 : #a56390,
|
||||
400 : #924179,
|
||||
500 : #7f2061,
|
||||
600 : #771c59,
|
||||
700 : #6c184f,
|
||||
800 : #621345,
|
||||
900 : #4f0b33,
|
||||
A100 : #ff85c7,
|
||||
A200 : #ff52b0,
|
||||
A400 : #ff1f99,
|
||||
A700 : #ff068d,
|
||||
contrast: (
|
||||
50 : #000000,
|
||||
100 : #000000,
|
||||
200 : #000000,
|
||||
300 : #ffffff,
|
||||
400 : #ffffff,
|
||||
500 : #ffffff,
|
||||
600 : #ffffff,
|
||||
700 : #ffffff,
|
||||
800 : #ffffff,
|
||||
900 : #ffffff,
|
||||
A100 : #000000,
|
||||
A200 : #000000,
|
||||
A400 : #ffffff,
|
||||
A700 : #ffffff,
|
||||
)
|
||||
);
|
||||
|
||||
$accent: (
|
||||
50 : #ffffff,
|
||||
100 : #ffffff,
|
||||
200 : #ffffff,
|
||||
300 : #ffffff,
|
||||
400 : #ffffff,
|
||||
500 : #ffffff,
|
||||
600 : #ffffff,
|
||||
700 : #ffffff,
|
||||
800 : #ffffff,
|
||||
900 : #ffffff,
|
||||
A100 : #ffffff,
|
||||
A200 : #ffffff,
|
||||
A400 : #ffffff,
|
||||
A700 : #ffffff,
|
||||
contrast: (
|
||||
50 : #000000,
|
||||
100 : #000000,
|
||||
200 : #000000,
|
||||
300 : #000000,
|
||||
400 : #000000,
|
||||
500 : #000000,
|
||||
600 : #000000,
|
||||
700 : #000000,
|
||||
800 : #000000,
|
||||
900 : #000000,
|
||||
A100 : #000000,
|
||||
A200 : #000000,
|
||||
A400 : #000000,
|
||||
A700 : #000000,
|
||||
)
|
||||
);
|
||||
|
||||
$primary-palette: mat.define-palette($primary);
|
||||
$accent-palette: mat.define-palette($accent);
|
||||
|
||||
$theme: mat.define-light-theme((
|
||||
color: (
|
||||
primary: $primary-palette,
|
||||
accent: $accent-palette,
|
||||
),
|
||||
typography: mat.define-typography-config(),
|
||||
density: 0,
|
||||
));
|
||||
|
||||
@include mat.all-component-themes($theme);
|
||||
|
||||
// Сброс стилей
|
||||
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:middle}
|
||||
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;}
|
||||
article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}
|
||||
html{height:100%;}
|
||||
body{line-height:1}
|
||||
@ -27,7 +111,7 @@ body {
|
||||
}
|
||||
|
||||
:root {
|
||||
--main-color: #00b26b;
|
||||
--main-color: #7F2061;
|
||||
--main-border-radius: 35px;
|
||||
--background-color: #ffffff;
|
||||
|
||||
@ -39,6 +123,32 @@ body {
|
||||
|
||||
--button-text-color: #ffffff;
|
||||
--button-text-color_disabled: #cccccc;
|
||||
|
||||
--main-color_hover: rgba(127, 32, 97, 0.3);
|
||||
}
|
||||
|
||||
.mdc-button__label {
|
||||
color: var(--button-text-color) !important;
|
||||
}
|
||||
|
||||
.mat-mdc-outlined-button:not(:disabled) {
|
||||
border-color: var(--button-text-color) !important;
|
||||
}
|
||||
|
||||
.mat-h1,
|
||||
.mat-headline-5,
|
||||
.mat-typography .mat-h1,
|
||||
.mat-typography .mat-headline-5,
|
||||
.mat-typography h1 {
|
||||
font-family: Montserrat;
|
||||
}
|
||||
|
||||
.mat-h2,
|
||||
.mat-headline-6,
|
||||
.mat-typography .mat-h2,
|
||||
.mat-typography .mat-headline-6,
|
||||
.mat-typography h2 {
|
||||
font-family: Montserrat, sans-serif;
|
||||
}
|
||||
|
||||
hr {
|
||||
@ -46,64 +156,6 @@ hr {
|
||||
border-top: 1px solid #BDBDBD;
|
||||
}
|
||||
|
||||
.mat-bottom-sheet-container {
|
||||
box-shadow: 0px 8px 10px -5px rgba(255, 255, 255, 0.2), 0px 16px 24px 2px rgba(255, 255, 255, 0.14), 0px 6px 30px 5px rgba(255, 255, 255, 0.12);
|
||||
background: var(--background-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
|
||||
.mat-form-field-appearance-outline .mat-form-field-outline {
|
||||
color: var(--button-color_disabled) !important;
|
||||
}
|
||||
|
||||
.mat-form-field-appearance-outline .mat-form-field-outline {
|
||||
color: var(--button-color) !important;
|
||||
}
|
||||
|
||||
.mat-form-field-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.mat-progress-spinner circle, .mat-spinner circle {
|
||||
stroke: var(--button-color);
|
||||
}
|
||||
|
||||
.mat-form-field-outline-start, .mat-form-field-outline-end {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
.mat-form-field-outline-start {
|
||||
border-left: none !important;
|
||||
}
|
||||
.mat-form-field-outline-end {
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
.mat-focused .mat-form-field-outline > div {
|
||||
border-color: var(--button-color);
|
||||
}
|
||||
|
||||
.mat-form-field-label {
|
||||
color: var(--text-color_1) !important;
|
||||
}
|
||||
|
||||
.mat-focused .mat-form-field-label {
|
||||
color: var(--text-color) !important;
|
||||
}
|
||||
|
||||
.mat-form-field-invalid .mat-form-field-outline > div {
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.country-list-button {
|
||||
color: var(--text-color) !important;
|
||||
}
|
||||
|
||||
.mat-menu-panel {
|
||||
background: var(--background-color);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.country-selector {
|
||||
opacity: 1 !important;
|
||||
display: none !important;
|
||||
@ -117,7 +169,6 @@ hr {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
|
||||
qr-code canvas {
|
||||
transition: all 0.3s ease 0s;
|
||||
}
|
||||
@ -175,5 +226,21 @@ input::-webkit-date-and-time-value {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
.p-toast-message-custom {
|
||||
background-color: var(--background-color);
|
||||
border: solid var(--main-color);
|
||||
border-width: 0 0 0 6px;
|
||||
color: var(--text-color);
|
||||
|
||||
.p-toast-icon-close {
|
||||
color: var(--main-color);
|
||||
}
|
||||
}
|
||||
|
||||
.p-toast.p-component.p-toast-top-center {
|
||||
max-width: 96vw;
|
||||
}
|
||||
|
||||
html, body { height: 100%; }
|
||||
body { margin: 0; }
|
||||
|
||||
@ -7,20 +7,8 @@ import {
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: {
|
||||
context(path: string, deep?: boolean, filter?: RegExp): {
|
||||
<T>(id: string): T;
|
||||
keys(): string[];
|
||||
};
|
||||
};
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting(),
|
||||
);
|
||||
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().forEach(context);
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"target": "es2020",
|
||||
"target": "ES2022",
|
||||
"module": "es2020",
|
||||
"lib": [
|
||||
"es2020",
|
||||
@ -24,6 +24,7 @@
|
||||
],
|
||||
"resolveJsonModule": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"useDefineForClassFields": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user