diff --git a/angular/angular.json b/angular/angular.json index 76e1e77..e10ef21 100644 --- a/angular/angular.json +++ b/angular/angular.json @@ -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" diff --git a/angular/package-lock.json b/angular/package-lock.json index 4b1a00f..275ecff 100644 --- a/angular/package-lock.json +++ b/angular/package-lock.json @@ -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", diff --git a/angular/package.json b/angular/package.json index d3d91df..abd0f1d 100644 --- a/angular/package.json +++ b/angular/package.json @@ -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", diff --git a/angular/proxy.confi.json b/angular/proxy.confi.json new file mode 100644 index 0000000..d5a8fdc --- /dev/null +++ b/angular/proxy.confi.json @@ -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" + } +} \ No newline at end of file diff --git a/angular/src/app/app.module.ts b/angular/src/app/app.module.ts index c48e111..be101cf 100644 --- a/angular/src/app/app.module.ts +++ b/angular/src/app/app.module.ts @@ -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 {} diff --git a/angular/src/app/components/exit/exit.component.html b/angular/src/app/components/exit/exit.component.html index d28addb..cf977a7 100644 --- a/angular/src/app/components/exit/exit.component.html +++ b/angular/src/app/components/exit/exit.component.html @@ -1,13 +1,7 @@ -
-

Вы действительно хотите выйти?

- - + + Вы действительно хотите выйти? + +
+ +
diff --git a/angular/src/app/components/exit/exit.component.scss b/angular/src/app/components/exit/exit.component.scss index 97f229d..c288335 100644 --- a/angular/src/app/components/exit/exit.component.scss +++ b/angular/src/app/components/exit/exit.component.scss @@ -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); } \ No newline at end of file diff --git a/angular/src/app/components/exit/exit.component.ts b/angular/src/app/components/exit/exit.component.ts index 1ad8e0b..70c4b59 100644 --- a/angular/src/app/components/exit/exit.component.ts +++ b/angular/src/app/components/exit/exit.component.ts @@ -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 + ) {} - onClick(val: boolean): void { - this.dialogRef.close(val); + rejection() { + this._bottomSheetRef.dismiss(false) } + logout() { + this._bottomSheetRef.dismiss(true) + } } diff --git a/angular/src/app/components/navbar/navbar.component.html b/angular/src/app/components/navbar/navbar.component.html index af71b4c..6717b43 100644 --- a/angular/src/app/components/navbar/navbar.component.html +++ b/angular/src/app/components/navbar/navbar.component.html @@ -1,5 +1,5 @@
- +

{{title}}

diff --git a/angular/src/app/components/navbar/navbar.component.scss b/angular/src/app/components/navbar/navbar.component.scss index 3e52122..dcd7a52 100644 --- a/angular/src/app/components/navbar/navbar.component.scss +++ b/angular/src/app/components/navbar/navbar.component.scss @@ -1,3 +1,7 @@ +:host { + width: 100%; +} + .container { box-sizing: border-box; padding: 12px 16px; diff --git a/angular/src/app/components/navbar/navbar.component.ts b/angular/src/app/components/navbar/navbar.component.ts index c58fad0..f0300d0 100644 --- a/angular/src/app/components/navbar/navbar.component.ts +++ b/angular/src/app/components/navbar/navbar.component.ts @@ -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(); constructor() { } ngOnInit(): void { } + back() { + this.backEvent.emit(null) + } + } diff --git a/angular/src/app/directives/focus-next-input.directive.spec.ts b/angular/src/app/directives/focus-next-input.directive.spec.ts new file mode 100644 index 0000000..1253457 --- /dev/null +++ b/angular/src/app/directives/focus-next-input.directive.spec.ts @@ -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(); + }); +}); diff --git a/angular/src/app/directives/focus-next-input.directive.ts b/angular/src/app/directives/focus-next-input.directive.ts new file mode 100644 index 0000000..3371901 --- /dev/null +++ b/angular/src/app/directives/focus-next-input.directive.ts @@ -0,0 +1,23 @@ +import { Directive, EventEmitter, Input, Renderer2 } from '@angular/core'; + +@Directive({ + selector: '[appFocusNextInput]' +}) +export class FocusNextInputDirective { + @Input('appFocusNextInput') eventEmitter!: EventEmitter; + + 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 + } + }); + } + +} diff --git a/angular/src/app/pages/guest-card/guest-card.component.html b/angular/src/app/pages/guest-card/guest-card.component.html index abf93cf..41df275 100644 --- a/angular/src/app/pages/guest-card/guest-card.component.html +++ b/angular/src/app/pages/guest-card/guest-card.component.html @@ -1,134 +1,153 @@ - -
-
- + + + + + +
+
+ + +
+
+ За период с 11.01.2023 по 31.03.2023 вам начислено + 360 бонусов +
+ +

+ Расчет начисления бонусов - 10% от суммы покупок за период с + 11.01.2023г. по 31.03.2023 г. +

+

+ За период с 11.01.2023г. по 31.03.2023 г. сумма ваших покупок составила + 3700 руб. +

+

Начисляемый бонус 10% от суммы покупок

+
+ +

+ Участник может использовать Бонусы для «оплаты» до 100% стоимости любой + покупки. +

+

+ Списание Бонусов происходит из расчета 1:1 (один Бонус дает скидку 1 + российский рубль / 1 тенге / 1 белорусский рубль. Скидка, + предоставляемая Участнику при списании Бонусов, уменьшает цену товаров в + заказе в соответствии с условиями ПЛ. +

+

+ Для списания Бонусов Участник должен попросить об этом в кофе-баре сети + «COFFEE LIKE» кассира до момента пробития фискального чека, после чего + им будет проверена возможность списания Бонусов. +

+

+ Для всех Участников возможно списание без использования мобильного + приложения. +

+

Полученные Бонусы не подлежат обмену на денежные средства.

+
+ +

+ Начисленные на счет бонусы сгорают по прошествии 90 дней с момента + совершения последней покупки с начислением или списанием бонусов. +

+ +
    + Возврат покупки, за которую бонусы были начислены: +
  • + В случае, если бонусов на счету достаточно для списания, бонусы + списываются в полном ранее начисленном за возвращаемый товар объеме. +
  • +
  • + В случае, если бонусов на счету недостаточно, формируется минусовой + баланс. +
  • +
+ +
    + Возврат покупки, которая была оплачена бонусами: +
  • + В случае предъявления Участником кассового или товарного чека, сумма + бонусов, списанная для оплаты возвращаемого товара, зачисляется на + счет участника. +
  • +
  • + В случае возврата товара с применением оплаты бонусами, клиенту + возвращается денежная сумма в размере, внесенном Участником в оплату + товара при покупке, за вычетом суммы, оплаченной бонусами. +
  • +
+
+ +
+ Сумма ваших покупок за период с 01.04.2023г. - 1200 руб. +
+ +

+ Начисление Бонусных баллов происходит по дифференцированной шкале в + зависимости от уровня: +

+ +
    + Уровень 1 +
  • Сумма покупок за предыдущий период 0-1600 руб.
  • +
  • Начисляемый бонус 3% от суммы покупки
  • +
+
+
    + Уровень 2 +
  • Сумма покупок за предыдущий период 1601-3600 руб.
  • +
  • Начисляемый бонус 6% от суммы покупки
  • +
+
+
    + Уровень 3 +
  • Сумма покупок за предыдущий период  3601-8600 руб.
  • +
  • Начисляемый бонус 10% от суммы покупки
  • +
+
+
    + Уровень 4 +
  • Сумма покупок за предыдущий период — от 8601 руб.
  • +
  • Начисляемый бонус, в % от суммы покупки - 15%
  • +
+
+ +
+

+ До следующего уровня за период с 01.04.2023 по 30.06.2023г осталось + совершить покупки на 401 рублей +

+ + +

Узнать условия начисления бонусов

+
+
+ +
+ +
+
+ Скачай приложение +
+ Подробнее о правилах
+ Программы лояльности
-
- За период с 11.01.2023 по 31.03.2023 вам начислено 360 бонусов -
- -

- Расчет начисления бонусов - 10% от суммы покупок за период с 11.01.2023г. - по 31.03.2023 г. -

-

- За период с 11.01.2023г. по 31.03.2023 г. сумма ваших покупок составила - 3700 руб. -

-

Начисляемый бонус 10% от суммы покупок

-
- -

- Участник может использовать Бонусы для «оплаты» до 100% стоимости любой - покупки. -

-

- Списание Бонусов происходит из расчета 1:1 (один Бонус дает скидку 1 - российский рубль / 1 тенге / 1 белорусский рубль. Скидка, предоставляемая - Участнику при списании Бонусов, уменьшает цену товаров в заказе в - соответствии с условиями ПЛ. -

-

- Для списания Бонусов Участник должен попросить об этом в кофе-баре сети - «COFFEE LIKE» кассира до момента пробития фискального чека, после чего им - будет проверена возможность списания Бонусов. -

-

- Для всех Участников возможно списание без использования мобильного - приложения. -

-

Полученные Бонусы не подлежат обмену на денежные средства.

-
- -

- Начисленные на счет бонусы сгорают по прошествии 90 дней с момента - совершения последней покупки с начислением или списанием бонусов. -

- -
    - Возврат покупки, за которую бонусы были начислены: -
  • - В случае, если бонусов на счету достаточно для списания, бонусы - списываются в полном ранее начисленном за возвращаемый товар объеме. -
  • -
  • - В случае, если бонусов на счету недостаточно, формируется минусовой - баланс. -
  • -
- -
    - Возврат покупки, которая была оплачена бонусами: -
  • - В случае предъявления Участником кассового или товарного чека, сумма - бонусов, списанная для оплаты возвращаемого товара, зачисляется на счет - участника. -
  • -
  • - В случае возврата товара с применением оплаты бонусами, клиенту - возвращается денежная сумма в размере, внесенном Участником в оплату - товара при покупке, за вычетом суммы, оплаченной бонусами. -
  • -
-
- -
- Сумма ваших покупок за период с 01.04.2023г. - 1200 руб. -
- -

- Начисление Бонусных баллов происходит по дифференцированной шкале в - зависимости от уровня: -

- -
    - Уровень 1 -
  • Сумма покупок за предыдущий период 0-1600 руб.
  • -
  • Начисляемый бонус 3% от суммы покупки
  • -
-
-
    - Уровень 2 -
  • Сумма покупок за предыдущий период 1601-3600 руб.
  • -
  • Начисляемый бонус 6% от суммы покупки
  • -
-
-
    - Уровень 3 -
  • Сумма покупок за предыдущий период  3601-8600 руб.
  • -
  • Начисляемый бонус 10% от суммы покупки
  • -
-
-
    - Уровень 4 -
  • Сумма покупок за предыдущий период — от 8601 руб.
  • -
  • Начисляемый бонус, в % от суммы покупки - 15%
  • -
-
- -
-

- До следующего уровня за период с 01.04.2023 по 30.06.2023г осталось - совершить покупки на 401 рублей -

- - -

Узнать условия начисления бонусов

-
-
- -
- -
-
- Скачай приложение -
- Подробнее о правилах
Программы лояльности
-
+ diff --git a/angular/src/app/pages/guest-card/guest-card.component.ts b/angular/src/app/pages/guest-card/guest-card.component.ts index 1ea6044..5c39173 100644 --- a/angular/src/app/pages/guest-card/guest-card.component.ts +++ b/angular/src/app/pages/guest-card/guest-card.component.ts @@ -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; + public purchases!: Observable; 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 { + const token = this.cookiesService.getItem('token') + const delta = moment(end).diff(moment(start), 'days') + return this.wpJsonService.getTransactionsInfo(environment.systemId, token ?? '', environment.icardProxy) + } } diff --git a/angular/src/app/pages/login/login.component.html b/angular/src/app/pages/login/login.component.html index 7f6f7f2..d05ebb6 100644 --- a/angular/src/app/pages/login/login.component.html +++ b/angular/src/app/pages/login/login.component.html @@ -1,26 +1,99 @@ +

Участвуй в программе лояльности COFFEE LIKE

Начни получать бонусы прямо сейчас

-
+ + + + Ваше имя + + +
- - -
-
- - + + + Номер телефона + + +

Используя приложение, вы принимаете условия в соглашениях и соглашаетесь на получение рекламно-информационных сообщений

- +

Введите код из SMS

-
- + +
+ + + + +
-

Не пришло SMS?
Отправим повторно через секунд

+

Не пришло SMS?
Отправим повторно через секунд

diff --git a/angular/src/app/pages/login/login.component.scss b/angular/src/app/pages/login/login.component.scss index 2f200ea..431a4f6 100644 --- a/angular/src/app/pages/login/login.component.scss +++ b/angular/src/app/pages/login/login.component.scss @@ -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%; +} diff --git a/angular/src/app/pages/login/login.component.ts b/angular/src/app/pages/login/login.component.ts index a1ff1fc..0e31c2a 100644 --- a/angular/src/app/pages/login/login.component.ts +++ b/angular/src/app/pages/login/login.component.ts @@ -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(); + + @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 } } diff --git a/angular/src/app/services/wp-json.service.ts b/angular/src/app/services/wp-json.service.ts index 9071beb..4e91efe 100644 --- a/angular/src/app/services/wp-json.service.ts +++ b/angular/src/app/services/wp-json.service.ts @@ -42,7 +42,27 @@ export class WpJsonService { return this._request(`products/${id}`, 'GET'); } - _request(path: string, method: string, body?: any, auth = false): Observable { + getCustomerInfo(systemId: string, token: string, url: string): Observable { + return this._request(`customer_info/${systemId}/${token}/`, 'GET', null, false, url) + } + + getTransactions(systemId: string, token: string, url: string, delta?: number): Observable { + return this._request(`trans/${systemId}/${token}/${delta || ''}`, 'GET', null, false, url) + } + + getTransactionsInfo(systemId: string, token: string, url: string, delta?: number): Observable { + return this._request(`purchase/${systemId}/${token}/${delta || ''}`, 'GET', null, false, url) + } + + // getSiteConfig(): Observable { + // return this._request(`/assets/site-config.json`, 'GET', null, false) + // } + + // getSiteConfigFromIiko(): Observable { + // return this._request(`/static/settings.json`, 'GET', null, false) + // } + + _request(path: string, method: string, body?: any, auth = false, baseUrl?: string): Observable { 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); } diff --git a/angular/src/environments/environment.prod.ts b/angular/src/environments/environment.prod.ts index ca311d1..86f5ab5 100644 --- a/angular/src/environments/environment.prod.ts +++ b/angular/src/environments/environment.prod.ts @@ -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' } diff --git a/angular/src/environments/environment.ts b/angular/src/environments/environment.ts index 6059fbc..060fb78 100644 --- a/angular/src/environments/environment.ts +++ b/angular/src/environments/environment.ts @@ -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' }; diff --git a/angular/src/styles.scss b/angular/src/styles.scss index d7d2e69..9a2ffb2 100644 --- a/angular/src/styles.scss +++ b/angular/src/styles.scss @@ -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; }