припилил СА, iikocard (но пока только customer_info), доработки
This commit is contained in:
gofnnp 2023-04-25 19:10:14 +04:00
parent 1da7556aed
commit af60b55e94
22 changed files with 773 additions and 208 deletions

View File

@ -87,7 +87,8 @@
"browserTarget": "coffee-like:build:production" "browserTarget": "coffee-like:build:production"
}, },
"development": { "development": {
"browserTarget": "coffee-like:build:development" "browserTarget": "coffee-like:build:development",
"proxyConfig": "proxy.confi.json"
} }
}, },
"defaultConfiguration": "development" "defaultConfiguration": "development"

View File

@ -30,7 +30,9 @@
"firebase": "^9.9.3", "firebase": "^9.9.3",
"google-libphonenumber": "^3.2.30", "google-libphonenumber": "^3.2.30",
"jsbarcode": "^3.11.5", "jsbarcode": "^3.11.5",
"libphonenumber-js": "^1.10.28",
"ng-qrcode": "^7.0.0", "ng-qrcode": "^7.0.0",
"ngx-mat-intl-tel-input": "^5.0.0",
"ngx-sharebuttons": "^11.0.0", "ngx-sharebuttons": "^11.0.0",
"primeicons": "^5.0.0", "primeicons": "^5.0.0",
"primeng": "^14.0.1", "primeng": "^14.0.1",
@ -8862,6 +8864,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/libphonenumber-js": {
"version": "1.10.28",
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.28.tgz",
"integrity": "sha512-1eAgjLrZA0+2Wgw4hs+4Q/kEBycxQo8ZLYnmOvZ3AlM8ImAVAJgDPlZtISLEzD1vunc2q8s2Pn7XwB7I8U3Kzw=="
},
"node_modules/license-webpack-plugin": { "node_modules/license-webpack-plugin": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz",
@ -9636,6 +9643,22 @@
"@angular/core": ">=14 <15" "@angular/core": ">=14 <15"
} }
}, },
"node_modules/ngx-mat-intl-tel-input": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ngx-mat-intl-tel-input/-/ngx-mat-intl-tel-input-5.0.0.tgz",
"integrity": "sha512-3XwA0zzcxBF/p+BQqBuGxPa7Mi+upehbrR6WvgwWcd+T9NcjImNEAf/Pd8R37OarN3fLrxFq/8g6B2zqYZtBCg==",
"dependencies": {
"tslib": "^2.x"
},
"peerDependencies": {
"@angular/common": ">=14.x",
"@angular/core": ">=14.x",
"@angular/forms": ">=14.x",
"@angular/platform-browser": ">=14.x",
"@angular/platform-browser-dynamic": ">=14.x",
"libphonenumber-js": "^1.10.11"
}
},
"node_modules/ngx-sharebuttons": { "node_modules/ngx-sharebuttons": {
"version": "11.0.0", "version": "11.0.0",
"resolved": "https://registry.npmjs.org/ngx-sharebuttons/-/ngx-sharebuttons-11.0.0.tgz", "resolved": "https://registry.npmjs.org/ngx-sharebuttons/-/ngx-sharebuttons-11.0.0.tgz",
@ -20274,6 +20297,11 @@
"klona": "^2.0.4" "klona": "^2.0.4"
} }
}, },
"libphonenumber-js": {
"version": "1.10.28",
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.28.tgz",
"integrity": "sha512-1eAgjLrZA0+2Wgw4hs+4Q/kEBycxQo8ZLYnmOvZ3AlM8ImAVAJgDPlZtISLEzD1vunc2q8s2Pn7XwB7I8U3Kzw=="
},
"license-webpack-plugin": { "license-webpack-plugin": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz",
@ -20862,6 +20890,14 @@
"tslib": "^2.4.0" "tslib": "^2.4.0"
} }
}, },
"ngx-mat-intl-tel-input": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ngx-mat-intl-tel-input/-/ngx-mat-intl-tel-input-5.0.0.tgz",
"integrity": "sha512-3XwA0zzcxBF/p+BQqBuGxPa7Mi+upehbrR6WvgwWcd+T9NcjImNEAf/Pd8R37OarN3fLrxFq/8g6B2zqYZtBCg==",
"requires": {
"tslib": "^2.x"
}
},
"ngx-sharebuttons": { "ngx-sharebuttons": {
"version": "11.0.0", "version": "11.0.0",
"resolved": "https://registry.npmjs.org/ngx-sharebuttons/-/ngx-sharebuttons-11.0.0.tgz", "resolved": "https://registry.npmjs.org/ngx-sharebuttons/-/ngx-sharebuttons-11.0.0.tgz",

View File

@ -32,7 +32,9 @@
"firebase": "^9.9.3", "firebase": "^9.9.3",
"google-libphonenumber": "^3.2.30", "google-libphonenumber": "^3.2.30",
"jsbarcode": "^3.11.5", "jsbarcode": "^3.11.5",
"libphonenumber-js": "^1.10.28",
"ng-qrcode": "^7.0.0", "ng-qrcode": "^7.0.0",
"ngx-mat-intl-tel-input": "^5.0.0",
"ngx-sharebuttons": "^11.0.0", "ngx-sharebuttons": "^11.0.0",
"primeicons": "^5.0.0", "primeicons": "^5.0.0",
"primeng": "^14.0.1", "primeng": "^14.0.1",

38
angular/proxy.confi.json Normal file
View File

@ -0,0 +1,38 @@
{
"/api": {
"target": "https://apple-push-notifications.it-retail.tech/apns/api",
"secure": false,
"pathRewrite": {
"^/api": ""
},
"changeOrigin": true,
"logLevel": "debug"
},
"/icard-proxy": {
"target": "https://sakura.lk.crm4retail.ru/api/icard-proxy",
"secure": false,
"pathRewrite": {
"^/icard-proxy": ""
},
"changeOrigin": true,
"logLevel": "debug"
},
"/static": {
"target": "https://sakura.lk.crm4retail.ru/static",
"secure": false,
"pathRewrite": {
"^/static": ""
},
"changeOrigin": true,
"logLevel": "debug"
},
"/it-retail": {
"target": "https://sakura.lk.crm4retail.ru/it-retail",
"secure": false,
"pathRewrite": {
"^/it-retail": ""
},
"changeOrigin": true,
"logLevel": "debug"
}
}

View File

@ -7,9 +7,9 @@ import { AppComponent } from './app.component';
import { MainComponent } from './pages/main/main.component'; import { MainComponent } from './pages/main/main.component';
import { NavbarComponent } from './components/navbar/navbar.component'; import { NavbarComponent } from './components/navbar/navbar.component';
import { CardComponent } from './components/card/card.component'; import { CardComponent } from './components/card/card.component';
import {InputMaskModule} from "primeng/inputmask"; import { InputMaskModule } from 'primeng/inputmask';
import { AuthComponent } from './pages/account/auth/auth.component'; import { AuthComponent } from './pages/account/auth/auth.component';
import {ProgressSpinnerModule} from "primeng/progressspinner"; import { ProgressSpinnerModule } from 'primeng/progressspinner';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AccountComponent } from './pages/account/account.component'; import { AccountComponent } from './pages/account/account.component';
import { ExitComponent } from './components/exit/exit.component'; import { ExitComponent } from './components/exit/exit.component';
@ -42,6 +42,16 @@ import { InviteFriendsComponent } from './components/invite-friends/invite-frien
import { FooterComponent } from './components/footer/footer.component'; import { FooterComponent } from './components/footer/footer.component';
import { SocialMediaButtonsComponent } from './components/social-media-buttons/social-media-buttons.component'; import { SocialMediaButtonsComponent } from './components/social-media-buttons/social-media-buttons.component';
import { LoginComponent } from './pages/login/login.component'; import { LoginComponent } from './pages/login/login.component';
import { NgxMatIntlTelInputComponent } from 'ngx-mat-intl-tel-input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { FocusNextInputDirective } from './directives/focus-next-input.directive';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import {
MAT_BOTTOM_SHEET_DATA,
MatBottomSheetModule,
MatBottomSheetRef,
} from '@angular/material/bottom-sheet';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -66,7 +76,8 @@ import { LoginComponent } from './pages/login/login.component';
InviteFriendsComponent, InviteFriendsComponent,
FooterComponent, FooterComponent,
SocialMediaButtonsComponent, SocialMediaButtonsComponent,
LoginComponent LoginComponent,
FocusNextInputDirective,
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
@ -81,20 +92,31 @@ import { LoginComponent } from './pages/login/login.component';
enabled: environment.production, enabled: environment.production,
// Register the ServiceWorker as soon as the application is stable // Register the ServiceWorker as soon as the application is stable
// or after 30 seconds (whichever comes first). // or after 30 seconds (whichever comes first).
registrationStrategy: 'registerWhenStable:30000' registrationStrategy: 'registerWhenStable:30000',
}), }),
AngularFireModule.initializeApp(environment.firebase), AngularFireModule.initializeApp(environment.firebase),
AngularFireMessagingModule, AngularFireMessagingModule,
ToastModule, ToastModule,
ReactiveFormsModule, ReactiveFormsModule,
ShareButtonsModule.withConfig({ ShareButtonsModule.withConfig({
debug: true debug: true,
}), }),
ShareIconsModule, ShareIconsModule,
MatIconModule, MatIconModule,
QrCodeModule QrCodeModule,
NgxMatIntlTelInputComponent,
MatFormFieldModule,
MatInputModule,
MatSnackBarModule,
MatBottomSheetModule,
], ],
providers: [DialogService, MessageService, MessagingService ], providers: [
bootstrap: [AppComponent] DialogService,
MessageService,
MessagingService,
{ provide: MatBottomSheetRef, useValue: {} },
{ provide: MAT_BOTTOM_SHEET_DATA, useValue: {} },
],
bootstrap: [AppComponent],
}) })
export class AppModule {} export class AppModule {}

View File

@ -1,13 +1,7 @@
<div> <span class="example-pizza-party" matSnackBarLabel>
<H2>Вы действительно хотите выйти?</H2> Вы действительно хотите выйти?
<button </span>
class="woocommerce-button button" <div class="buttons-container">
style="margin-right: 1rem" <button mat-button (click)="rejection()" style="background: red;">Нет</button>
(click)="onClick(true)" <button mat-button (click)="logout()">Да</button>
>
Да
</button>
<button class="woocommerce-button button" (click)="onClick(false)">
Нет
</button>
</div> </div>

View File

@ -1,9 +1,22 @@
:host {
display: flex;
flex-direction: column;
align-items: center;
.buttons-container {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
}
}
button { button {
margin-right: 1rem; padding: 10px 62px;
padding: 4px 21px;
margin-top: 8px; margin-top: 8px;
background-color: var(--main-color); background-color: var(--main-color);
color: #fff; color: #fff;
border-radius: 3px; border-radius: 3px;
border: none; border: none;
width: calc(50% - 12px);
} }

View File

@ -1,19 +1,21 @@
import { Component, OnInit } from '@angular/core'; import { Component } from '@angular/core';
import { DynamicDialogRef } from 'primeng/dynamicdialog'; import { MatBottomSheetRef } from '@angular/material/bottom-sheet';
@Component({ @Component({
selector: 'app-exit', selector: 'app-exit',
templateUrl: './exit.component.html', templateUrl: './exit.component.html',
styleUrls: ['./exit.component.scss'] styleUrls: ['./exit.component.scss'],
}) })
export class ExitComponent { export class ExitComponent {
constructor( constructor(
public dialogRef: DynamicDialogRef private _bottomSheetRef: MatBottomSheetRef<ExitComponent>
) {} ) {}
onClick(val: boolean): void { rejection() {
this.dialogRef.close(val); this._bottomSheetRef.dismiss(false)
} }
logout() {
this._bottomSheetRef.dismiss(true)
}
} }

View File

@ -1,5 +1,5 @@
<div class="container"> <div class="container">
<mat-icon aria-hidden="false" aria-label="Назад" fontIcon="arrow_back_ios" class="back-arrow"></mat-icon> <mat-icon aria-hidden="false" aria-label="Назад" fontIcon="arrow_back_ios" class="back-arrow" (click)="back()"></mat-icon>
<h1 class="title">{{title}}</h1> <h1 class="title">{{title}}</h1>
<div class="plug"></div> <div class="plug"></div>
</div> </div>

View File

@ -1,3 +1,7 @@
:host {
width: 100%;
}
.container { .container {
box-sizing: border-box; box-sizing: border-box;
padding: 12px 16px; padding: 12px 16px;

View File

@ -1,4 +1,4 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
@Component({ @Component({
selector: 'app-navbar[title]', selector: 'app-navbar[title]',
@ -7,10 +7,15 @@ import { Component, Input, OnInit } from '@angular/core';
}) })
export class NavbarComponent implements OnInit { export class NavbarComponent implements OnInit {
@Input() title: string = 'Название не задано' @Input() title: string = 'Название не задано'
@Output() backEvent = new EventEmitter<null>();
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {
} }
back() {
this.backEvent.emit(null)
}
} }

View File

@ -0,0 +1,8 @@
import { FocusNextInputDirective } from './focus-next-input.directive';
describe('FocusNextInputDirective', () => {
it('should create an instance', () => {
const directive = new FocusNextInputDirective();
expect(directive).toBeTruthy();
});
});

View File

@ -0,0 +1,23 @@
import { Directive, EventEmitter, Input, Renderer2 } from '@angular/core';
@Directive({
selector: '[appFocusNextInput]'
})
export class FocusNextInputDirective {
@Input('appFocusNextInput') eventEmitter!: EventEmitter<string>;
constructor(private renderer: Renderer2) { }
ngOnInit() {
this.eventEmitter.subscribe(elementId => {
try {
this.renderer.selectRootElement(elementId).focus();
this.renderer.selectRootElement(elementId).click();
} 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
}
});
}
}

View File

@ -1,20 +1,28 @@
<app-navbar title="Карта гостя"></app-navbar> <app-navbar title="Карта гостя" (backEvent)="logout()"></app-navbar>
<ng-container
*ngTemplateOutlet="appTpl; context: { user: customerInfo | async }"
></ng-container>
<ng-template #appTpl let-user="user">
<div class="guest-card"> <div class="guest-card">
<div class="guest-card__qr" (click)="qrCodeClick()"> <div class="guest-card__qr" (click)="qrCodeClick()">
<qr-code <qr-code
value="9917997225" [value]="user?.customer_info?.phone.substr(2) || 'Данные не найдены'"
[margin]="0" [margin]="0"
[size]="qrCodeSize" [size]="qrCodeSize"
errorCorrectionLevel="M" errorCorrectionLevel="M"
></qr-code> ></qr-code>
</div> </div>
<div class="guest-card__user-description"> <div class="guest-card__user-description">
За период с 11.01.2023 по 31.03.2023 вам начислено <span>360 бонусов</span> За период с 11.01.2023 по 31.03.2023 вам начислено
<span>360 бонусов</span>
</div> </div>
<app-accordion header="Условия начисления бонусов"> <app-accordion header="Условия начисления бонусов">
<p> <p>
Расчет начисления бонусов - 10% от суммы покупок за период с 11.01.2023г. Расчет начисления бонусов - 10% от суммы покупок за период с
по 31.03.2023 г. 11.01.2023г. по 31.03.2023 г.
</p> </p>
<p> <p>
За период с 11.01.2023г. по 31.03.2023 г. сумма ваших покупок составила За период с 11.01.2023г. по 31.03.2023 г. сумма ваших покупок составила
@ -29,14 +37,14 @@
</p> </p>
<p> <p>
Списание Бонусов происходит из расчета 1:1 (один Бонус дает скидку 1 Списание Бонусов происходит из расчета 1:1 (один Бонус дает скидку 1
российский рубль / 1 тенге / 1 белорусский рубль. Скидка, предоставляемая российский рубль / 1 тенге / 1 белорусский рубль. Скидка,
Участнику при списании Бонусов, уменьшает цену товаров в заказе в предоставляемая Участнику при списании Бонусов, уменьшает цену товаров в
соответствии с условиями ПЛ. заказе в соответствии с условиями ПЛ.
</p> </p>
<p> <p>
Для списания Бонусов Участник должен попросить об этом в кофе-баре сети Для списания Бонусов Участник должен попросить об этом в кофе-баре сети
«COFFEE LIKE» кассира до момента пробития фискального чека, после чего им «COFFEE LIKE» кассира до момента пробития фискального чека, после чего
будет проверена возможность списания Бонусов. им будет проверена возможность списания Бонусов.
</p> </p>
<p> <p>
Для всех Участников возможно списание без использования мобильного Для всех Участников возможно списание без использования мобильного
@ -66,8 +74,8 @@
Возврат покупки, которая была оплачена бонусами: Возврат покупки, которая была оплачена бонусами:
<li> <li>
В случае предъявления Участником кассового или товарного чека, сумма В случае предъявления Участником кассового или товарного чека, сумма
бонусов, списанная для оплаты возвращаемого товара, зачисляется на счет бонусов, списанная для оплаты возвращаемого товара, зачисляется на
участника. счет участника.
</li> </li>
<li> <li>
В случае возврата товара с применением оплаты бонусами, клиенту В случае возврата товара с применением оплаты бонусами, клиенту
@ -116,19 +124,30 @@
До следующего уровня за период с 01.04.2023 по 30.06.2023г осталось До следующего уровня за период с 01.04.2023 по 30.06.2023г осталось
совершить покупки на 401 рублей совершить покупки на 401 рублей
</h2> </h2>
<input type="range" [(ngModel)]="discountLevel" [min]="3" [max]="6" [step]="0.1" [ngStyle]="{ <input
'background-size': (discountLevel - 3) / (6 - 3) * 100 + '% 100%' type="range"
}"> [(ngModel)]="discountLevel"
[min]="3"
[max]="6"
[step]="0.1"
[ngStyle]="{
'background-size': ((discountLevel - 3) / (6 - 3)) * 100 + '% 100%'
}"
/>
<p class="show-more">Узнать условия начисления бонусов</p> <p class="show-more">Узнать условия начисления бонусов</p>
</div> </div>
<hr> <hr />
<app-last-order></app-last-order> <app-last-order></app-last-order>
<hr> <hr />
<app-invite-friends></app-invite-friends> <app-invite-friends></app-invite-friends>
<hr> <hr />
<div class="guest-card__download-app"> <div class="guest-card__download-app">
<img src="/assets/download-app.svg" alt="Скачай приложение"> <img src="/assets/download-app.svg" alt="Скачай приложение" />
</div> </div>
<a class="guest-card__loyalty-program" routerLink="loyalty-program">Подробнее о правилах <br> Программы лояльности</a> <a class="guest-card__loyalty-program" routerLink="loyalty-program"
>Подробнее о правилах <br />
Программы лояльности</a
>
</div> </div>
</ng-template>

View File

@ -1,5 +1,16 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { IAccordionData } from 'src/app/components/accordion/accordion.component'; 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';
interface Moment extends moment.Moment {
}
@Component({ @Component({
selector: 'app-guest-card', selector: 'app-guest-card',
@ -9,15 +20,49 @@ import { IAccordionData } from 'src/app/components/accordion/accordion.component
export class GuestCardComponent implements OnInit { export class GuestCardComponent implements OnInit {
public qrCodeSize: number = 85; public qrCodeSize: number = 85;
private isQrCodeClicked: boolean = false; private isQrCodeClicked: boolean = false;
public customerInfo!: Observable<any>;
public purchases!: Observable<any>;
public discountLevel: number = 4.2; public discountLevel: number = 4.2;
constructor() {} constructor(
private _bottomSheet: MatBottomSheet,
private cookiesService: CookiesService,
private router: Router,
private wpJsonService: WpJsonService
) {}
ngOnInit(): void {} ngOnInit(): void {
const token = this.cookiesService.getItem('token')
this.customerInfo = this.wpJsonService.getCustomerInfo(environment.systemId, token || '', environment.icardProxy)
this.purchases = this.getPurchases()
// this.purchases.subscribe((value) => console.log(value));
}
qrCodeClick() { qrCodeClick() {
this.isQrCodeClicked = !this.isQrCodeClicked; this.isQrCodeClicked = !this.isQrCodeClicked;
this.qrCodeSize = this.isQrCodeClicked ? 180 : 85; this.qrCodeSize = this.isQrCodeClicked ? 180 : 85;
} }
deleteToken(): void {
this.cookiesService.deleteCookie('token');
}
logout() {
const bottomSheet = this._bottomSheet.open(ExitComponent)
bottomSheet.afterDismissed().subscribe({
next: (val) => {
if (val) {
this.deleteToken();
this.router.navigate(['/login']);
}
}
})
}
getPurchases(start: Date | Moment = moment().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.getTransactionsInfo(environment.systemId, token ?? '', environment.icardProxy)
}
} }

View File

@ -1,26 +1,99 @@
<app-navbar
*ngIf="phoneForm.value.phone && !isShowNumber"
[title]="phoneForm.value.phone"
(backEvent)="backToPhoneForm()"
[ngStyle]="{
position: 'absolute',
top: 0
}"
></app-navbar>
<h1>Участвуй в программе лояльности COFFEE LIKE</h1> <h1>Участвуй в программе лояльности COFFEE LIKE</h1>
<p class="description">Начни получать бонусы прямо сейчас</p> <p class="description">Начни получать бонусы прямо сейчас</p>
<form *ngIf="isShowNumber; else smsCode" (ngSubmit)="submitNumber()" action=""> <form
*ngIf="isShowNumber; else smsCode"
(ngSubmit)="submitNumber()"
[formGroup]="phoneForm"
>
<!-- <div class="input-container"> -->
<mat-form-field appearance="outline">
<mat-label>Ваше имя</mat-label>
<input formControlName="name" matInput type="text" />
</mat-form-field>
<!-- </div> -->
<div class="input-container"> <div class="input-container">
<label for="name">Ваше имя</label> <!-- <label for="number">Номер телефона</label>
<input id="name" type="text" placeholder="Введите ваше имя" /> <input id="number" type="text" placeholder="Введите номер" /> -->
</div> <mat-form-field appearance="outline">
<div class="input-container"> <mat-label>Номер телефона</mat-label>
<label for="number">Номер телефона</label> <ngx-mat-intl-tel-input
<input id="number" type="text" placeholder="Введите номер" /> formControlName="phone"
[enablePlaceholder]="true"
[enableSearch]="false"
[onlyCountries]="['ru', 'kg', 'by', 'kz', 'fi', 'de']"
[preferredCountries]="['ru']"
name="phone"
#phone
>
</ngx-mat-intl-tel-input>
</mat-form-field>
</div> </div>
<p class="offer"> <p class="offer">
Используя приложение, вы принимаете условия в <span>соглашениях</span> и Используя приложение, вы принимаете условия в <span>соглашениях</span> и
соглашаетесь на получение рекламно-информационных сообщений соглашаетесь на получение рекламно-информационных сообщений
</p> </p>
<button>Принять участие</button> <button [disabled]="phoneForm.invalid">Принять участие</button>
</form> </form>
<ng-template #smsCode> <ng-template #smsCode>
<h2>Введите код из SMS</h2> <h2>Введите код из SMS</h2>
<form class="code-form" action="" (ngSubmit)="submitCode()"> <form class="code-form" [formGroup]="codeForm" (ngSubmit)="submitCode()">
<input type="text" placeholder="Код" /> <div class="inputs-container">
<label class="box"
><input
class="field"
id="field"
type="tel"
placeholder="•"
#field
[appFocusNextInput]="inputFocusEmitter"
formControlName="code"
maxlength="1"
/></label>
<label class="box"
><input
class="field"
id="field1"
type="tel"
placeholder="•"
#field1
[appFocusNextInput]="inputFocusEmitter"
formControlName="code1"
maxlength="1"
/></label>
<label class="box"
><input
class="field"
id="field2"
type="tel"
placeholder="•"
#field2
[appFocusNextInput]="inputFocusEmitter"
formControlName="code2"
maxlength="1"
/></label>
<label class="box"
><input
class="field"
id="field3"
type="tel"
placeholder="•"
#field3
[appFocusNextInput]="inputFocusEmitter"
formControlName="code3"
maxlength="1"
/></label>
</div>
<button>Войти</button> <button>Войти</button>
</form> </form>
<p class="resend-code" >Не пришло SMS?<br>Отправим повторно через секунд</p> <p class="resend-code">Не пришло SMS?<br />Отправим повторно через секунд</p>
</ng-template> </ng-template>

View File

@ -7,6 +7,7 @@
margin: 0 auto 52px; margin: 0 auto 52px;
h1 { h1 {
margin-top: 20px;
width: 302px; width: 302px;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
@ -106,24 +107,92 @@
font-weight: 700; font-weight: 700;
font-size: 17px; font-size: 17px;
line-height: 22px; line-height: 22px;
&:disabled {
background-color: #344a3a;
}
} }
} }
.code-form { .code-form {
width: 100%; width: 100%;
input { // input {
// width: 100%;
// padding: 24px 16px 8px;
// background-color: #252323;
// margin-bottom: 16px;
// border: none;
// border-top: solid #6a737c 1px;
// border-bottom: solid #6a737c 1px;
// color: #6a737c;
// font-style: normal;
// font-weight: 400;
// font-size: 22px;
// line-height: 28px;
// }
.inputs-container {
width: 102px;
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 40px;
// code
.box {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 42px;
width: 42px;
border-radius: 6px;
// box-shadow: 0 0 6px 1px hsla(240, 54%, 61%, 0.2);
overflow: hidden;
will-change: transform;
}
.box:focus-within {
box-shadow: 0 0 6px 1px rgba($color: #28af49, $alpha: 0.2),
0 0 0 2px rgba($color: #28af49, $alpha: 0.6);
}
.box::before,
.box::after {
content: "";
position: absolute;
height: 100%;
width: 100%; width: 100%;
padding: 24px 16px 8px; top: 0;
background-color: #252323; left: 0;
margin-bottom: 16px; border-radius: 6px;
border: none; overflow: hidden;
border-top: solid #6a737c 1px; }
border-bottom: solid #6a737c 1px; .box::before {
color: #6a737c; // background: hsl(240, 54%, 97%);
font-style: normal; z-index: 1;
font-weight: 400; transition: background-color 450ms cubic-bezier(0.25, 0.01, 0.25, 1);
font-size: 22px; }
line-height: 28px; .box::after {
transform: translateY(100%);
background-color: hsl(145, 0%, 42%);
opacity: 0;
z-index: 10;
transition: transform 450ms cubic-bezier(0.25, 0.01, 0.25, 1),
opacity 450ms cubic-bezier(0.25, 0.01, 0.25, 1),
background-color 450ms cubic-bezier(0.25, 0.01, 0.25, 1);
}
.field {
position: relative;
border: 0;
outline: 0;
font-size: 25.21px;
line-height: 42px;
color: #fff;
background-color: transparent;
text-align: center;
z-index: 100;
}
.field::placeholder {
color: #fff;
}
} }
} }
@ -133,5 +202,10 @@
font-size: 12px; font-size: 12px;
line-height: 16px; line-height: 16px;
text-align: center; text-align: center;
margin-top: 23px;
} }
} }
mat-form-field {
width: 100%;
}

View File

@ -1,25 +1,158 @@
import { Component, OnInit } from '@angular/core'; import {
AfterViewInit,
Component,
EventEmitter,
HostListener,
OnInit
} from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { MessageService } from 'primeng/api';
import { CookiesService } from 'src/app/services/cookies.service'; import { CookiesService } from 'src/app/services/cookies.service';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
@Component({ @Component({
selector: 'app-login', selector: 'app-login',
templateUrl: './login.component.html', templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'], styleUrls: ['./login.component.scss'],
}) })
export class LoginComponent implements OnInit { export class LoginComponent implements OnInit, AfterViewInit {
isShowNumber: boolean = true; public isShowNumber: boolean = true;
public phoneForm = new FormGroup({
name: new FormControl('', [Validators.required]),
phone: new FormControl('', [Validators.required]),
});
public codeForm = new FormGroup({
code: new FormControl('', [Validators.required]),
code1: new FormControl('', [Validators.required]),
code2: new FormControl('', [Validators.required]),
code3: new FormControl('', [Validators.required]),
});
private inputIds = ['field', 'field1', 'field2', 'field3'];
constructor(private cookiesService: CookiesService, private router: Router) {} constructor(
private cookiesService: CookiesService,
private router: Router,
private jsonrpc: JsonrpcService,
private messageService: MessageService
) {}
ngOnInit(): void {} ngOnInit(): void {}
ngAfterViewInit() {
setTimeout(() => {
this.inputFocusEmitter.emit(`#${this.inputIds[0]}`);
}, 1000)
}
public inputFocusEmitter = new EventEmitter<string>();
@HostListener('window:keyup', ['$event'])
HandlKeyEvents(event: any) {
if (!event.target.classList.contains('field')) return;
const key = event.key.toLocaleLowerCase();
let elementId = '';
switch (key) {
case 'backspace':
elementId = event.target.id;
event.target.value = '';
const prevInputIndex = this.inputIds.indexOf(elementId) - 1;
if (prevInputIndex >= 0) {
this.inputFocusEmitter.emit(`#${this.inputIds[prevInputIndex]}`);
}
break;
default:
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);
}
if (nextInputIndex > 0 && nextInputIndex <= this.inputIds.length) {
this.inputFocusEmitter.emit(`#${this.inputIds[nextInputIndex]}`);
}
break;
}
}
submitNumber() { submitNumber() {
const data = this.phoneForm.value;
console.log(data);
this.isShowNumber = false; this.isShowNumber = false;
this.jsonrpc.rpc({
method: 'sendVerifyByPhone',
params: [data.phone]
}, RpcService.authService, false).subscribe({
next: (result) => {
if (result.code === -1) {
this.messageService.add({
severity: 'error',
summary: 'Произошла ошибка, попробуйте позже!',
});
}
// if (result.code === 0) {
// this.isCodeConfirm = true;
// this.timeLeft = 60;
// const interval = setInterval(() => {
// if(this.timeLeft > 0) {
// this.timeLeft--;
// } else {
// clearInterval(interval);
// }
// },1000)
// }
// this.loading = false;
this.isShowNumber = false;
},
error: (error) => {
console.error('Error: ', error);
}
}
);
setTimeout(() => {
this.inputFocusEmitter.emit(`#${this.inputIds[0]}`);
}, 0)
} }
submitCode() { submitCode() {
this.cookiesService.setCookie('token', 'test') const data = this.codeForm.value;
this.router.navigate(['/']) 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.router.navigate(['/'], {
queryParams: {
token: result?.data?.token
},
});
// this.phoneConfirmed.emit(null);
} else {
// this.errorConfirmCode = true;
}
},
error: (error) => {
console.error(error);
}
}
);
}
backToPhoneForm() {
this.codeForm.setValue({
code: '',
code1: '',
code2: '',
code3: ''
})
this.isShowNumber = true
} }
} }

View File

@ -42,7 +42,27 @@ export class WpJsonService {
return this._request(`products/${id}`, 'GET'); return this._request(`products/${id}`, 'GET');
} }
_request(path: string, method: string, body?: any, auth = false): Observable<any> { getCustomerInfo(systemId: string, token: string, url: string): Observable<any> {
return this._request(`customer_info/${systemId}/${token}/`, 'GET', null, false, url)
}
getTransactions(systemId: string, token: string, url: string, delta?: number): Observable<any> {
return this._request(`trans/${systemId}/${token}/${delta || ''}`, 'GET', null, false, url)
}
getTransactionsInfo(systemId: string, token: string, url: string, delta?: number): Observable<any> {
return this._request(`purchase/${systemId}/${token}/${delta || ''}`, 'GET', null, false, url)
}
// getSiteConfig(): Observable<any> {
// return this._request(`/assets/site-config.json`, 'GET', null, false)
// }
// getSiteConfigFromIiko(): Observable<any> {
// return this._request(`/static/settings.json`, 'GET', null, false)
// }
_request(path: string, method: string, body?: any, auth = false, baseUrl?: string): Observable<any> {
const token = decodeURI(this.cookiesService.getItem('token') ?? ''); const token = decodeURI(this.cookiesService.getItem('token') ?? '');
let headers = new HttpHeaders(); let headers = new HttpHeaders();
headers = headers.set('Content-Type', 'application/json'); headers = headers.set('Content-Type', 'application/json');
@ -56,8 +76,10 @@ export class WpJsonService {
body: this.body, body: this.body,
}; };
const url = environment.production ? window.location.origin + '/wp-json/woofood/v1/' : this.api let url = environment.production ? window.location.origin + '/' : this.api
if (baseUrl) {
url = baseUrl
}
return this.http return this.http
.request( method, url + path + urlToken, options); .request( method, url + path + urlToken, options);
} }

View File

@ -6,7 +6,7 @@ export const environment = {
appBonusEndpoint: 'https://customerapi2.mi.crm4retail.ru/json.rpc/', appBonusEndpoint: 'https://customerapi2.mi.crm4retail.ru/json.rpc/',
appWPEndpoint: 'http://213.239.210.240:4500/wp-json/woofood/v1/', appWPEndpoint: 'http://213.239.210.240:4500/wp-json/woofood/v1/',
hasBonusProgram: true, hasBonusProgram: true,
systemId: 'g6zyv8tj53w28ov7cl', systemId: 'tsQ2cu59Xz9qgGTm3z',
defaultUrl: 'https://coffee-like.lk.crm4retail.ru', defaultUrl: 'https://coffee-like.lk.crm4retail.ru',
firebase: { firebase: {
apiKey: "AIzaSyCnKvln5itnrBj62POCPHxshAN_Vmd0zds", apiKey: "AIzaSyCnKvln5itnrBj62POCPHxshAN_Vmd0zds",
@ -19,6 +19,7 @@ export const environment = {
}, },
version: packageJson.version, version: packageJson.version,
appleWalletEndpoint: 'https://apple-push-notifications.it-retail.tech/apns/api', appleWalletEndpoint: 'https://apple-push-notifications.it-retail.tech/apns/api',
icardProxy: 'https://coffee-like.lk.crm4retail.ru/api/icard-proxy/',
appleWalletSecret: 'Token F5mbzEERAznGKVbB6l', appleWalletSecret: 'Token F5mbzEERAznGKVbB6l',
clientName: 'coffeeLike' clientName: 'coffeeLike'
} }

View File

@ -6,7 +6,7 @@ export const environment = {
appBonusEndpoint: 'https://customerapi2.mi.crm4retail.ru/json.rpc/', appBonusEndpoint: 'https://customerapi2.mi.crm4retail.ru/json.rpc/',
appWPEndpoint: 'http://192.168.0.179:4200/wp-json/woofood/v1/', appWPEndpoint: 'http://192.168.0.179:4200/wp-json/woofood/v1/',
hasBonusProgram: true, hasBonusProgram: true,
systemId: 'g6zyv8tj53w28ov7cl', systemId: 'tsQ2cu59Xz9qgGTm3z',
defaultUrl: 'http://192.168.0.179:4200', defaultUrl: 'http://192.168.0.179:4200',
firebase: { firebase: {
apiKey: 'AIzaSyCnKvln5itnrBj62POCPHxshAN_Vmd0zds', apiKey: 'AIzaSyCnKvln5itnrBj62POCPHxshAN_Vmd0zds',
@ -19,6 +19,7 @@ export const environment = {
}, },
version: packageJson.version, version: packageJson.version,
appleWalletEndpoint: 'http://192.168.0.179:4200/apns/api', appleWalletEndpoint: 'http://192.168.0.179:4200/apns/api',
icardProxy: 'http://192.168.0.14:4200/icard-proxy/',
appleWalletSecret: 'Token F5mbzEERAznGKVbB6l', appleWalletSecret: 'Token F5mbzEERAznGKVbB6l',
clientName: 'coffeeLike' clientName: 'coffeeLike'
}; };

View File

@ -30,6 +30,55 @@ hr {
border-top: 1px solid#BDBDBD; border-top: 1px solid#BDBDBD;
} }
.mat-form-field-wrapper {
padding: 0;
}
.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: #28af49;
}
.mat-focused .mat-form-field-label {
color: #fff !important;
}
.mat-form-field-invalid .mat-form-field-outline > div {
border-color: red;
}
.country-list-button {
color: #fff !important;
}
.mat-menu-panel {
background: #231f20;
border-radius: 0;
}
.country-selector {
opacity: 1 !important;
display: none !important;
}
.ngx-floating .country-selector {
display: block !important;
}
.country-selector-code {
color: #fff;
}
qr-code canvas { qr-code canvas {
transition: all 0.3s ease 0s; transition: all 0.3s ease 0s;
} }