припилил СА, 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"
},
"development": {
"browserTarget": "coffee-like:build:development"
"browserTarget": "coffee-like:build:development",
"proxyConfig": "proxy.confi.json"
}
},
"defaultConfiguration": "development"

View File

@ -30,7 +30,9 @@
"firebase": "^9.9.3",
"google-libphonenumber": "^3.2.30",
"jsbarcode": "^3.11.5",
"libphonenumber-js": "^1.10.28",
"ng-qrcode": "^7.0.0",
"ngx-mat-intl-tel-input": "^5.0.0",
"ngx-sharebuttons": "^11.0.0",
"primeicons": "^5.0.0",
"primeng": "^14.0.1",
@ -8862,6 +8864,11 @@
"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": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz",
@ -9636,6 +9643,22 @@
"@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": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/ngx-sharebuttons/-/ngx-sharebuttons-11.0.0.tgz",
@ -20274,6 +20297,11 @@
"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": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz",
@ -20862,6 +20890,14 @@
"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": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/ngx-sharebuttons/-/ngx-sharebuttons-11.0.0.tgz",

View File

@ -32,7 +32,9 @@
"firebase": "^9.9.3",
"google-libphonenumber": "^3.2.30",
"jsbarcode": "^3.11.5",
"libphonenumber-js": "^1.10.28",
"ng-qrcode": "^7.0.0",
"ngx-mat-intl-tel-input": "^5.0.0",
"ngx-sharebuttons": "^11.0.0",
"primeicons": "^5.0.0",
"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 { NavbarComponent } from './components/navbar/navbar.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 {ProgressSpinnerModule} from "primeng/progressspinner";
import { ProgressSpinnerModule } from 'primeng/progressspinner';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AccountComponent } from './pages/account/account.component';
import { ExitComponent } from './components/exit/exit.component';
@ -23,17 +23,17 @@ import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment';
import { AngularFireModule } from '@angular/fire/compat';
import { AngularFireMessagingModule } from '@angular/fire/compat/messaging';
import {ToastModule} from 'primeng/toast';
import { ToastModule } from 'primeng/toast';
import { MessageService } from 'primeng/api';
import { FooterButtonsComponent } from './components/footer-buttons/footer-buttons.component';
import { UserDataComponent } from './pages/account/user-data/user-data.component';
import { RefSystemComponent } from './pages/account/ref-system/ref-system.component';
import { ShareButtonsModule } from 'ngx-sharebuttons/buttons';
import { ShareButtonsModule } from 'ngx-sharebuttons/buttons';
import { ShareIconsModule } from 'ngx-sharebuttons/icons';
import { MessagingService } from './services/messaging.service';
import { NotFoundComponent } from './pages/not-found/not-found.component';
import { DownloadAppDirective } from './directives/download-app.directive';
import {MatIconModule} from '@angular/material/icon';
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';
@ -42,6 +42,16 @@ import { InviteFriendsComponent } from './components/invite-friends/invite-frien
import { FooterComponent } from './components/footer/footer.component';
import { SocialMediaButtonsComponent } from './components/social-media-buttons/social-media-buttons.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({
declarations: [
@ -66,7 +76,8 @@ import { LoginComponent } from './pages/login/login.component';
InviteFriendsComponent,
FooterComponent,
SocialMediaButtonsComponent,
LoginComponent
LoginComponent,
FocusNextInputDirective,
],
imports: [
BrowserModule,
@ -81,20 +92,31 @@ import { LoginComponent } from './pages/login/login.component';
enabled: environment.production,
// Register the ServiceWorker as soon as the application is stable
// or after 30 seconds (whichever comes first).
registrationStrategy: 'registerWhenStable:30000'
registrationStrategy: 'registerWhenStable:30000',
}),
AngularFireModule.initializeApp(environment.firebase),
AngularFireMessagingModule,
ToastModule,
ReactiveFormsModule,
ShareButtonsModule.withConfig({
debug: true
debug: true,
}),
ShareIconsModule,
MatIconModule,
QrCodeModule
QrCodeModule,
NgxMatIntlTelInputComponent,
MatFormFieldModule,
MatInputModule,
MatSnackBarModule,
MatBottomSheetModule,
],
providers: [DialogService, MessageService, MessagingService ],
bootstrap: [AppComponent]
providers: [
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>
<H2>Вы действительно хотите выйти?</H2>
<button
class="woocommerce-button button"
style="margin-right: 1rem"
(click)="onClick(true)"
>
Да
</button>
<button class="woocommerce-button button" (click)="onClick(false)">
Нет
</button>
<span class="example-pizza-party" matSnackBarLabel>
Вы действительно хотите выйти?
</span>
<div class="buttons-container">
<button mat-button (click)="rejection()" style="background: red;">Нет</button>
<button mat-button (click)="logout()">Да</button>
</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 {
margin-right: 1rem;
padding: 4px 21px;
padding: 10px 62px;
margin-top: 8px;
background-color: var(--main-color);
color: #fff;
border-radius: 3px;
border: none;
width: calc(50% - 12px);
}

View File

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

View File

@ -1,5 +1,5 @@
<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>
<div class="plug"></div>
</div>

View File

@ -1,3 +1,7 @@
:host {
width: 100%;
}
.container {
box-sizing: border-box;
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({
selector: 'app-navbar[title]',
@ -7,10 +7,15 @@ import { Component, Input, OnInit } from '@angular/core';
})
export class NavbarComponent implements OnInit {
@Input() title: string = 'Название не задано'
@Output() backEvent = new EventEmitter<null>();
constructor() { }
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,134 +1,153 @@
<app-navbar title="Карта гостя"></app-navbar>
<div class="guest-card">
<div class="guest-card__qr" (click)="qrCodeClick()">
<qr-code
value="9917997225"
[margin]="0"
[size]="qrCodeSize"
errorCorrectionLevel="M"
></qr-code>
<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__qr" (click)="qrCodeClick()">
<qr-code
[value]="user?.customer_info?.phone.substr(2) || 'Данные не найдены'"
[margin]="0"
[size]="qrCodeSize"
errorCorrectionLevel="M"
></qr-code>
</div>
<div class="guest-card__user-description">
За период с 11.01.2023 по 31.03.2023 вам начислено
<span>360 бонусов</span>
</div>
<app-accordion header="Условия начисления бонусов">
<p>
Расчет начисления бонусов - 10% от суммы покупок за период с
11.01.2023г. по 31.03.2023 г.
</p>
<p>
За период с 11.01.2023г. по 31.03.2023 г. сумма ваших покупок составила
3700 руб.
</p>
<p>Начисляемый бонус 10% от суммы покупок</p>
</app-accordion>
<app-accordion header="Условия «оплаты» покупки бонусами">
<p>
Участник может использовать Бонусы для «оплаты» до 100% стоимости любой
покупки.
</p>
<p>
Списание Бонусов происходит из расчета 1:1 (один Бонус дает скидку 1
российский рубль / 1 тенге / 1 белорусский рубль. Скидка,
предоставляемая Участнику при списании Бонусов, уменьшает цену товаров в
заказе в соответствии с условиями ПЛ.
</p>
<p>
Для списания Бонусов Участник должен попросить об этом в кофе-баре сети
«COFFEE LIKE» кассира до момента пробития фискального чека, после чего
им будет проверена возможность списания Бонусов.
</p>
<p>
Для всех Участников возможно списание без использования мобильного
приложения.
</p>
<p>Полученные Бонусы не подлежат обмену на денежные средства.</p>
</app-accordion>
<app-accordion header="Особые условия">
<p>
Начисленные на счет бонусы сгорают по прошествии 90 дней с момента
совершения последней покупки с начислением или списанием бонусов.
</p>
<ul>
Возврат покупки, за которую бонусы были начислены:
<li>
В случае, если бонусов на счету достаточно для списания, бонусы
списываются в полном ранее начисленном за возвращаемый товар объеме.
</li>
<li>
В случае, если бонусов на счету недостаточно, формируется минусовой
баланс.
</li>
</ul>
<ul>
Возврат покупки, которая была оплачена бонусами:
<li>
В случае предъявления Участником кассового или товарного чека, сумма
бонусов, списанная для оплаты возвращаемого товара, зачисляется на
счет участника.
</li>
<li>
В случае возврата товара с применением оплаты бонусами, клиенту
возвращается денежная сумма в размере, внесенном Участником в оплату
товара при покупке, за вычетом суммы, оплаченной бонусами.
</li>
</ul>
</app-accordion>
<div class="guest-card__purchases-description">
Сумма ваших покупок за период с 01.04.2023г. - <span>1200 руб.</span>
</div>
<app-accordion header="Узнать % начисляемых бонусов">
<p>
Начисление Бонусных баллов происходит по дифференцированной шкале в
зависимости от уровня:
</p>
<ul>
<span style="color: #f2c94c">Уровень 1</span>
<li>Сумма покупок за предыдущий период 0-1600 руб.</li>
<li>Начисляемый бонус 3% от суммы покупки</li>
</ul>
<br />
<ul>
<span style="color: #f2994a">Уровень 2</span>
<li>Сумма покупок за предыдущий период 1601-3600 руб.</li>
<li>Начисляемый бонус 6% от суммы покупки</li>
</ul>
<br />
<ul>
<span style="color: #6fcf97">Уровень 3</span>
<li>Сумма покупок за предыдущий период  3601-8600 руб.</li>
<li>Начисляемый бонус 10% от суммы покупки</li>
</ul>
<br />
<ul>
<span style="color: #6fcf97">Уровень 4</span>
<li>Сумма покупок за предыдущий период — от 8601 руб.</li>
<li>Начисляемый бонус, в % от суммы покупки - 15%</li>
</ul>
</app-accordion>
<div class="guest-card__level-info">
<h2>
До следующего уровня за период с 01.04.2023 по 30.06.2023г осталось
совершить покупки на 401 рублей
</h2>
<input
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>
</div>
<hr />
<app-last-order></app-last-order>
<hr />
<app-invite-friends></app-invite-friends>
<hr />
<div class="guest-card__download-app">
<img src="/assets/download-app.svg" alt="Скачай приложение" />
</div>
<a class="guest-card__loyalty-program" routerLink="loyalty-program"
>Подробнее о правилах <br />
Программы лояльности</a
>
</div>
<div class="guest-card__user-description">
За период с 11.01.2023 по 31.03.2023 вам начислено <span>360 бонусов</span>
</div>
<app-accordion header="Условия начисления бонусов">
<p>
Расчет начисления бонусов - 10% от суммы покупок за период с 11.01.2023г.
по 31.03.2023 г.
</p>
<p>
За период с 11.01.2023г. по 31.03.2023 г. сумма ваших покупок составила
3700 руб.
</p>
<p>Начисляемый бонус 10% от суммы покупок</p>
</app-accordion>
<app-accordion header="Условия «оплаты» покупки бонусами">
<p>
Участник может использовать Бонусы для «оплаты» до 100% стоимости любой
покупки.
</p>
<p>
Списание Бонусов происходит из расчета 1:1 (один Бонус дает скидку 1
российский рубль / 1 тенге / 1 белорусский рубль. Скидка, предоставляемая
Участнику при списании Бонусов, уменьшает цену товаров в заказе в
соответствии с условиями ПЛ.
</p>
<p>
Для списания Бонусов Участник должен попросить об этом в кофе-баре сети
«COFFEE LIKE» кассира до момента пробития фискального чека, после чего им
будет проверена возможность списания Бонусов.
</p>
<p>
Для всех Участников возможно списание без использования мобильного
приложения.
</p>
<p>Полученные Бонусы не подлежат обмену на денежные средства.</p>
</app-accordion>
<app-accordion header="Особые условия">
<p>
Начисленные на счет бонусы сгорают по прошествии 90 дней с момента
совершения последней покупки с начислением или списанием бонусов.
</p>
<ul>
Возврат покупки, за которую бонусы были начислены:
<li>
В случае, если бонусов на счету достаточно для списания, бонусы
списываются в полном ранее начисленном за возвращаемый товар объеме.
</li>
<li>
В случае, если бонусов на счету недостаточно, формируется минусовой
баланс.
</li>
</ul>
<ul>
Возврат покупки, которая была оплачена бонусами:
<li>
В случае предъявления Участником кассового или товарного чека, сумма
бонусов, списанная для оплаты возвращаемого товара, зачисляется на счет
участника.
</li>
<li>
В случае возврата товара с применением оплаты бонусами, клиенту
возвращается денежная сумма в размере, внесенном Участником в оплату
товара при покупке, за вычетом суммы, оплаченной бонусами.
</li>
</ul>
</app-accordion>
<div class="guest-card__purchases-description">
Сумма ваших покупок за период с 01.04.2023г. - <span>1200 руб.</span>
</div>
<app-accordion header="Узнать % начисляемых бонусов">
<p>
Начисление Бонусных баллов происходит по дифференцированной шкале в
зависимости от уровня:
</p>
<ul>
<span style="color: #f2c94c">Уровень 1</span>
<li>Сумма покупок за предыдущий период 0-1600 руб.</li>
<li>Начисляемый бонус 3% от суммы покупки</li>
</ul>
<br />
<ul>
<span style="color: #f2994a">Уровень 2</span>
<li>Сумма покупок за предыдущий период 1601-3600 руб.</li>
<li>Начисляемый бонус 6% от суммы покупки</li>
</ul>
<br />
<ul>
<span style="color: #6fcf97">Уровень 3</span>
<li>Сумма покупок за предыдущий период  3601-8600 руб.</li>
<li>Начисляемый бонус 10% от суммы покупки</li>
</ul>
<br />
<ul>
<span style="color: #6fcf97">Уровень 4</span>
<li>Сумма покупок за предыдущий период — от 8601 руб.</li>
<li>Начисляемый бонус, в % от суммы покупки - 15%</li>
</ul>
</app-accordion>
<div class="guest-card__level-info">
<h2>
До следующего уровня за период с 01.04.2023 по 30.06.2023г осталось
совершить покупки на 401 рублей
</h2>
<input 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>
</div>
<hr>
<app-last-order></app-last-order>
<hr>
<app-invite-friends></app-invite-friends>
<hr>
<div class="guest-card__download-app">
<img src="/assets/download-app.svg" alt="Скачай приложение">
</div>
<a class="guest-card__loyalty-program" routerLink="loyalty-program">Подробнее о правилах <br> Программы лояльности</a>
</div>
</ng-template>

View File

@ -1,5 +1,16 @@
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({
selector: 'app-guest-card',
@ -9,15 +20,49 @@ import { IAccordionData } from 'src/app/components/accordion/accordion.component
export class GuestCardComponent implements OnInit {
public qrCodeSize: number = 85;
private isQrCodeClicked: boolean = false;
public customerInfo!: Observable<any>;
public purchases!: Observable<any>;
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() {
this.isQrCodeClicked = !this.isQrCodeClicked;
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>
<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">
<label for="name">Ваше имя</label>
<input id="name" type="text" placeholder="Введите ваше имя" />
</div>
<div class="input-container">
<label for="number">Номер телефона</label>
<input id="number" type="text" placeholder="Введите номер" />
<!-- <label for="number">Номер телефона</label>
<input id="number" type="text" placeholder="Введите номер" /> -->
<mat-form-field appearance="outline">
<mat-label>Номер телефона</mat-label>
<ngx-mat-intl-tel-input
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>
<p class="offer">
Используя приложение, вы принимаете условия в <span>соглашениях</span> и
соглашаетесь на получение рекламно-информационных сообщений
</p>
<button>Принять участие</button>
<button [disabled]="phoneForm.invalid">Принять участие</button>
</form>
<ng-template #smsCode>
<h2>Введите код из SMS</h2>
<form class="code-form" action="" (ngSubmit)="submitCode()">
<input type="text" placeholder="Код" />
<form class="code-form" [formGroup]="codeForm" (ngSubmit)="submitCode()">
<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>
</form>
<p class="resend-code" >Не пришло SMS?<br>Отправим повторно через секунд</p>
<p class="resend-code">Не пришло SMS?<br />Отправим повторно через секунд</p>
</ng-template>

View File

@ -7,6 +7,7 @@
margin: 0 auto 52px;
h1 {
margin-top: 20px;
width: 302px;
font-style: normal;
font-weight: 700;
@ -106,24 +107,92 @@
font-weight: 700;
font-size: 17px;
line-height: 22px;
&:disabled {
background-color: #344a3a;
}
}
}
.code-form {
width: 100%;
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;
// 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%;
top: 0;
left: 0;
border-radius: 6px;
overflow: hidden;
}
.box::before {
// background: hsl(240, 54%, 97%);
z-index: 1;
transition: background-color 450ms cubic-bezier(0.25, 0.01, 0.25, 1);
}
.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;
line-height: 16px;
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 { 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 {
isShowNumber: boolean = true;
export class LoginComponent implements OnInit, AfterViewInit {
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 {}
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() {
const data = this.phoneForm.value;
console.log(data);
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() {
this.cookiesService.setCookie('token', 'test')
this.router.navigate(['/'])
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.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');
}
_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') ?? '');
let headers = new HttpHeaders();
headers = headers.set('Content-Type', 'application/json');
@ -56,8 +76,10 @@ export class WpJsonService {
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
.request( method, url + path + urlToken, options);
}

View File

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

View File

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

View File

@ -30,6 +30,55 @@ hr {
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 {
transition: all 0.3s ease 0s;
}