From 86ac20f5e5debedf9bf352a2cb0a78ec0e4d6b18 Mon Sep 17 00:00:00 2001 From: gofnnp Date: Fri, 12 May 2023 20:10:06 +0400 Subject: [PATCH] =?UTF-8?q?dev=20#13995=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=BB=20=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80?= =?UTF-8?q?=D1=83=20=D0=BF=D0=BE=D0=B4=20=D0=B0=D0=B1=20=D1=82=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5,=20?= =?UTF-8?q?=D1=81=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=202=20=D1=80=D0=B0=D0=B7?= =?UTF-8?q?=D0=BB=D0=B8=D1=87=D0=BD=D1=8B=D1=85=20=D0=B2=D0=B0=D1=80=D0=B8?= =?UTF-8?q?=D0=B0=D0=BD=D1=82=20(=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=B4=D0=B5?= =?UTF-8?q?=D1=84=D0=BE=D0=BB=D1=82=D0=BD=D1=8B=D0=B9,=20=D0=B4=D1=80?= =?UTF-8?q?=D1=83=D0=B3=D0=BE=D0=B9=20=D1=81=20=D0=BA=D1=80=D0=B0=D1=81?= =?UTF-8?q?=D0=BD=D1=8B=D0=BC=20=D1=82=D0=B5=D0=BA=D1=81=D1=82=D0=BE=D0=BC?= =?UTF-8?q?)=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D0=B8=20=D0=B7=D0=B0=D0=B3?= =?UTF-8?q?=D1=80=D1=83=D0=B6=D0=B0=D1=8E=D1=82=D1=81=D1=8F=20=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B2=D0=BE,=20=D0=B4=D0=BB=D1=8F=20=D0=BE=D0=BF?= =?UTF-8?q?=D1=82=D0=B8=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B8=20=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D1=81=D0=B8=D0=BB=20=D0=BF=D0=BE=D0=B4=D1=81=D1=87?= =?UTF-8?q?=D0=B5=D1=82=20=D1=81=D1=83=D0=BC=D0=BC=D1=8B=20=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=B7=D0=B0=D0=BA=D1=86=D0=B8=D0=B9=20=D0=B7=D0=B0?= =?UTF-8?q?=20=D0=BF=D0=B5=D1=80=D0=B8=D0=BE=D0=B4=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- angular/src/app/app-routing.module.ts | 7 +- angular/src/app/app.component.ts | 56 ++++- angular/src/app/app.module.ts | 6 +- .../src/app/directives/directives.module.ts | 12 + .../directives/download-app.directive.spec.ts | 8 - .../focus-next-input.directive.spec.ts | 8 - angular/src/app/interface/data.ts | 2 + .../app/pages/account/account.component.ts | 2 +- .../guest-card/guest-card.component.scss | 1 + .../pages/guest-card/guest-card.component.ts | 18 +- .../accordion/accordion.component.html | 7 + .../accordion/accordion.component.scss | 85 +++++++ .../accordion/accordion.component.spec.ts | 23 ++ .../accordion/accordion.component.ts | 23 ++ .../components/footer/footer.component.html | 4 + .../components/footer/footer.component.scss | 27 +++ .../footer/footer.component.spec.ts | 23 ++ .../components/footer/footer.component.ts | 15 ++ .../components/index/index.component.html | 2 + .../components/index/index.component.scss | 0 .../components/index/index.component.spec.ts | 23 ++ .../components/index/index.component.ts | 15 ++ .../invite-friends.component.html | 25 ++ .../invite-friends.component.scss | 33 +++ .../invite-friends.component.spec.ts | 23 ++ .../invite-friends.component.ts | 54 +++++ .../last-order/last-order.component.html | 31 +++ .../last-order/last-order.component.scss | 51 +++++ .../last-order/last-order.component.spec.ts | 23 ++ .../last-order/last-order.component.ts | 18 ++ .../components/navbar/navbar.component.html | 5 + .../components/navbar/navbar.component.scss | 33 +++ .../navbar/navbar.component.spec.ts | 23 ++ .../components/navbar/navbar.component.ts | 21 ++ .../social-media-buttons.component.html | 3 + .../social-media-buttons.component.scss | 15 ++ .../social-media-buttons.component.spec.ts | 23 ++ .../social-media-buttons.component.ts | 38 ++++ .../default-option-routing.module.ts | 24 ++ .../default-option/default-option.module.ts | 49 ++++ .../guest-card/guest-card.component.html | 198 ++++++++++++++++ .../guest-card/guest-card.component.scss | 199 ++++++++++++++++ .../guest-card/guest-card.component.spec.ts | 23 ++ .../pages/guest-card/guest-card.component.ts | 99 ++++++++ .../pages/login/login.component.html | 107 +++++++++ .../pages/login/login.component.scss | 214 +++++++++++++++++ .../pages/login/login.component.spec.ts | 23 ++ .../pages/login/login.component.ts | 167 ++++++++++++++ .../accordion/accordion.component.html | 7 + .../accordion/accordion.component.scss | 85 +++++++ .../accordion/accordion.component.spec.ts | 23 ++ .../accordion/accordion.component.ts | 23 ++ .../components/footer/footer.component.html | 4 + .../components/footer/footer.component.scss | 27 +++ .../footer/footer.component.spec.ts | 23 ++ .../components/footer/footer.component.ts | 15 ++ .../components/index/index.component.html | 2 + .../components/index/index.component.scss | 3 + .../components/index/index.component.spec.ts | 23 ++ .../components/index/index.component.ts | 15 ++ .../invite-friends.component.html | 25 ++ .../invite-friends.component.scss | 33 +++ .../invite-friends.component.spec.ts | 23 ++ .../invite-friends.component.ts | 54 +++++ .../last-order/last-order.component.html | 31 +++ .../last-order/last-order.component.scss | 51 +++++ .../last-order/last-order.component.spec.ts | 23 ++ .../last-order/last-order.component.ts | 18 ++ .../components/navbar/navbar.component.html | 5 + .../components/navbar/navbar.component.scss | 33 +++ .../navbar/navbar.component.spec.ts | 23 ++ .../components/navbar/navbar.component.ts | 21 ++ .../social-media-buttons.component.html | 3 + .../social-media-buttons.component.scss | 15 ++ .../social-media-buttons.component.spec.ts | 23 ++ .../social-media-buttons.component.ts | 38 ++++ .../first-option-routing.module.ts | 23 ++ .../first-option/first-option.module.ts | 51 +++++ .../guest-card/guest-card.component.html | 198 ++++++++++++++++ .../guest-card/guest-card.component.scss | 200 ++++++++++++++++ .../guest-card/guest-card.component.spec.ts | 23 ++ .../pages/guest-card/guest-card.component.ts | 182 +++++++++++++++ .../pages/login/login.component.html | 107 +++++++++ .../pages/login/login.component.scss | 215 ++++++++++++++++++ .../pages/login/login.component.spec.ts | 23 ++ .../pages/login/login.component.ts | 167 ++++++++++++++ .../components/test/test.component.html | 1 + .../components/test/test.component.scss | 0 .../components/test/test.component.spec.ts | 23 ++ .../components/test/test.component.ts | 15 ++ .../second-option-routing.module.ts | 16 ++ .../second-option/second-option.module.ts | 17 ++ angular/src/app/services/cookies.service.ts | 6 + .../app/services/loyalty-program.service.ts | 128 +++++++++++ 94 files changed, 3920 insertions(+), 33 deletions(-) create mode 100644 angular/src/app/directives/directives.module.ts delete mode 100644 angular/src/app/directives/download-app.directive.spec.ts delete mode 100644 angular/src/app/directives/focus-next-input.directive.spec.ts create mode 100644 angular/src/app/presentation-options/default-option/components/accordion/accordion.component.html create mode 100644 angular/src/app/presentation-options/default-option/components/accordion/accordion.component.scss create mode 100644 angular/src/app/presentation-options/default-option/components/accordion/accordion.component.spec.ts create mode 100644 angular/src/app/presentation-options/default-option/components/accordion/accordion.component.ts create mode 100644 angular/src/app/presentation-options/default-option/components/footer/footer.component.html create mode 100644 angular/src/app/presentation-options/default-option/components/footer/footer.component.scss create mode 100644 angular/src/app/presentation-options/default-option/components/footer/footer.component.spec.ts create mode 100644 angular/src/app/presentation-options/default-option/components/footer/footer.component.ts create mode 100644 angular/src/app/presentation-options/default-option/components/index/index.component.html create mode 100644 angular/src/app/presentation-options/default-option/components/index/index.component.scss create mode 100644 angular/src/app/presentation-options/default-option/components/index/index.component.spec.ts create mode 100644 angular/src/app/presentation-options/default-option/components/index/index.component.ts create mode 100644 angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.html create mode 100644 angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.scss create mode 100644 angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.spec.ts create mode 100644 angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.ts create mode 100644 angular/src/app/presentation-options/default-option/components/last-order/last-order.component.html create mode 100644 angular/src/app/presentation-options/default-option/components/last-order/last-order.component.scss create mode 100644 angular/src/app/presentation-options/default-option/components/last-order/last-order.component.spec.ts create mode 100644 angular/src/app/presentation-options/default-option/components/last-order/last-order.component.ts create mode 100644 angular/src/app/presentation-options/default-option/components/navbar/navbar.component.html create mode 100644 angular/src/app/presentation-options/default-option/components/navbar/navbar.component.scss create mode 100644 angular/src/app/presentation-options/default-option/components/navbar/navbar.component.spec.ts create mode 100644 angular/src/app/presentation-options/default-option/components/navbar/navbar.component.ts create mode 100644 angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.html create mode 100644 angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.scss create mode 100644 angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.spec.ts create mode 100644 angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.ts create mode 100644 angular/src/app/presentation-options/default-option/default-option-routing.module.ts create mode 100644 angular/src/app/presentation-options/default-option/default-option.module.ts create mode 100644 angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.html create mode 100644 angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.scss create mode 100644 angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.spec.ts create mode 100644 angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.ts create mode 100644 angular/src/app/presentation-options/default-option/pages/login/login.component.html create mode 100644 angular/src/app/presentation-options/default-option/pages/login/login.component.scss create mode 100644 angular/src/app/presentation-options/default-option/pages/login/login.component.spec.ts create mode 100644 angular/src/app/presentation-options/default-option/pages/login/login.component.ts create mode 100644 angular/src/app/presentation-options/first-option/components/accordion/accordion.component.html create mode 100644 angular/src/app/presentation-options/first-option/components/accordion/accordion.component.scss create mode 100644 angular/src/app/presentation-options/first-option/components/accordion/accordion.component.spec.ts create mode 100644 angular/src/app/presentation-options/first-option/components/accordion/accordion.component.ts create mode 100644 angular/src/app/presentation-options/first-option/components/footer/footer.component.html create mode 100644 angular/src/app/presentation-options/first-option/components/footer/footer.component.scss create mode 100644 angular/src/app/presentation-options/first-option/components/footer/footer.component.spec.ts create mode 100644 angular/src/app/presentation-options/first-option/components/footer/footer.component.ts create mode 100644 angular/src/app/presentation-options/first-option/components/index/index.component.html create mode 100644 angular/src/app/presentation-options/first-option/components/index/index.component.scss create mode 100644 angular/src/app/presentation-options/first-option/components/index/index.component.spec.ts create mode 100644 angular/src/app/presentation-options/first-option/components/index/index.component.ts create mode 100644 angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.html create mode 100644 angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.scss create mode 100644 angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.spec.ts create mode 100644 angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.ts create mode 100644 angular/src/app/presentation-options/first-option/components/last-order/last-order.component.html create mode 100644 angular/src/app/presentation-options/first-option/components/last-order/last-order.component.scss create mode 100644 angular/src/app/presentation-options/first-option/components/last-order/last-order.component.spec.ts create mode 100644 angular/src/app/presentation-options/first-option/components/last-order/last-order.component.ts create mode 100644 angular/src/app/presentation-options/first-option/components/navbar/navbar.component.html create mode 100644 angular/src/app/presentation-options/first-option/components/navbar/navbar.component.scss create mode 100644 angular/src/app/presentation-options/first-option/components/navbar/navbar.component.spec.ts create mode 100644 angular/src/app/presentation-options/first-option/components/navbar/navbar.component.ts create mode 100644 angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.html create mode 100644 angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.scss create mode 100644 angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.spec.ts create mode 100644 angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.ts create mode 100644 angular/src/app/presentation-options/first-option/first-option-routing.module.ts create mode 100644 angular/src/app/presentation-options/first-option/first-option.module.ts create mode 100644 angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.html create mode 100644 angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.scss create mode 100644 angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.spec.ts create mode 100644 angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.ts create mode 100644 angular/src/app/presentation-options/first-option/pages/login/login.component.html create mode 100644 angular/src/app/presentation-options/first-option/pages/login/login.component.scss create mode 100644 angular/src/app/presentation-options/first-option/pages/login/login.component.spec.ts create mode 100644 angular/src/app/presentation-options/first-option/pages/login/login.component.ts create mode 100644 angular/src/app/presentation-options/second-option/components/test/test.component.html create mode 100644 angular/src/app/presentation-options/second-option/components/test/test.component.scss create mode 100644 angular/src/app/presentation-options/second-option/components/test/test.component.spec.ts create mode 100644 angular/src/app/presentation-options/second-option/components/test/test.component.ts create mode 100644 angular/src/app/presentation-options/second-option/second-option-routing.module.ts create mode 100644 angular/src/app/presentation-options/second-option/second-option.module.ts create mode 100644 angular/src/app/services/loyalty-program.service.ts diff --git a/angular/src/app/app-routing.module.ts b/angular/src/app/app-routing.module.ts index 0764816..1085081 100644 --- a/angular/src/app/app-routing.module.ts +++ b/angular/src/app/app-routing.module.ts @@ -8,13 +8,14 @@ import { AuthGuard } from './guards/auth-guard.guard'; const routes: Routes = [ { path: '', - component: GuestCardComponent, - canActivate: [AuthGuard] + loadChildren: () => null as any, + // component: GuestCardComponent, + // canActivate: [AuthGuard] }, { path: 'login', component: LoginComponent - } + }, // { path: '**', component: NotFoundComponent } ]; diff --git a/angular/src/app/app.component.ts b/angular/src/app/app.component.ts index 4fe43c3..519b236 100644 --- a/angular/src/app/app.component.ts +++ b/angular/src/app/app.component.ts @@ -1,10 +1,60 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { RouteConfigLoadStart, Router } from '@angular/router'; +import { CookiesService } from './services/cookies.service'; +import { JsonrpcService, RpcService } from './services/jsonrpc.service'; +import { lastValueFrom } from 'rxjs'; @Component({ selector: 'app-root', templateUrl: './app.component.html', - styleUrls: ['./app.component.scss'] + styleUrls: ['./app.component.scss'], }) -export class AppComponent { +export class AppComponent implements OnInit { title = 'Coffee Like'; + + constructor( + private router: Router, + private cookiesService: CookiesService, + private jsonRpcService: JsonrpcService + ) { + this.router.events.subscribe((x) => { + if (x instanceof RouteConfigLoadStart && x.route.path === '') { + x.route.loadChildren = () => { + switch (this.cookiesService.getItem('presentation-option')) { + case 'first': + return import( + './presentation-options/first-option/first-option.module' + ).then((mod) => mod.FirstOptionModule); + + default: + return import( + './presentation-options/default-option/default-option.module' + ).then((mod) => mod.DefaultOptionModule); + } + }; + } + }); + } + + getAdditionalInfo() { + return this.jsonRpcService.rpc( + { + method: 'getAdditionalInfo', + params: [], + }, + RpcService.authService, + true + ); + } + + ngOnInit(): void { + this.getAdditionalInfo().subscribe({ + next: (value) => { + this.cookiesService.setCookie('presentation-option', value?.data?.interface_id || 'default') + }, + error: (err) => { + console.error(err); + } + }) + } } diff --git a/angular/src/app/app.module.ts b/angular/src/app/app.module.ts index b689d55..c75d5e2 100644 --- a/angular/src/app/app.module.ts +++ b/angular/src/app/app.module.ts @@ -53,6 +53,7 @@ import { MatBottomSheetRef, } from '@angular/material/bottom-sheet'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; +import { DirectivesModule } from './directives/directives.module'; @NgModule({ declarations: [ @@ -78,7 +79,7 @@ import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; FooterComponent, SocialMediaButtonsComponent, LoginComponent, - FocusNextInputDirective, + // FocusNextInputDirective, ], imports: [ BrowserModule, @@ -110,7 +111,8 @@ import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; MatInputModule, MatSnackBarModule, MatBottomSheetModule, - MatProgressSpinnerModule + MatProgressSpinnerModule, + DirectivesModule ], providers: [ DialogService, diff --git a/angular/src/app/directives/directives.module.ts b/angular/src/app/directives/directives.module.ts new file mode 100644 index 0000000..7eed56e --- /dev/null +++ b/angular/src/app/directives/directives.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FocusNextInputDirective } from './focus-next-input.directive'; + +@NgModule({ + imports: [ + CommonModule + ], + declarations: [FocusNextInputDirective], + exports: [FocusNextInputDirective] +}) +export class DirectivesModule { } diff --git a/angular/src/app/directives/download-app.directive.spec.ts b/angular/src/app/directives/download-app.directive.spec.ts deleted file mode 100644 index e65de2e..0000000 --- a/angular/src/app/directives/download-app.directive.spec.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { DownloadAppDirective } from './download-app.directive'; - -describe('DownloadAppDirective', () => { - it('should create an instance', () => { - const directive = new DownloadAppDirective(); - expect(directive).toBeTruthy(); - }); -}); diff --git a/angular/src/app/directives/focus-next-input.directive.spec.ts b/angular/src/app/directives/focus-next-input.directive.spec.ts deleted file mode 100644 index 1253457..0000000 --- a/angular/src/app/directives/focus-next-input.directive.spec.ts +++ /dev/null @@ -1,8 +0,0 @@ -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/interface/data.ts b/angular/src/app/interface/data.ts index ddf3934..3b48ca3 100644 --- a/angular/src/app/interface/data.ts +++ b/angular/src/app/interface/data.ts @@ -6,6 +6,8 @@ export enum PageCode { UserData, } +export interface Moment extends moment.Moment {} + export interface Page { code: PageCode; component?: any; diff --git a/angular/src/app/pages/account/account.component.ts b/angular/src/app/pages/account/account.component.ts index 1075b88..c01bf67 100644 --- a/angular/src/app/pages/account/account.component.ts +++ b/angular/src/app/pages/account/account.component.ts @@ -166,7 +166,7 @@ export class AccountComponent implements OnInit { } deleteToken(): void { - this.cookiesService.deleteCookie('token'); + this.cookiesService.logout(); // this.router.navigate(['.'], { // queryParams: {}, // }); diff --git a/angular/src/app/pages/guest-card/guest-card.component.scss b/angular/src/app/pages/guest-card/guest-card.component.scss index 3795539..f3aab9b 100644 --- a/angular/src/app/pages/guest-card/guest-card.component.scss +++ b/angular/src/app/pages/guest-card/guest-card.component.scss @@ -124,6 +124,7 @@ width: 16px; height: 16px; border-radius: 100%; + display: none; } &::-ms-thumb { 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 1517d89..65c9d76 100644 --- a/angular/src/app/pages/guest-card/guest-card.component.ts +++ b/angular/src/app/pages/guest-card/guest-card.component.ts @@ -41,17 +41,17 @@ export class GuestCardComponent implements OnInit { $loading: true, get currentAmount():number { const amount = this.currentPurchases.reduce((accumulator, currentValue) => { - if (currentValue.transactionType !== 'PayFromWallet') return 0; - return accumulator + currentValue.transactionSum; + if (currentValue.transactionType !== 'PayFromWallet') return accumulator; + return accumulator + currentValue.orderSum!; }, 0); - return amount * -1 + return amount * 1 }, get lastAmount():number { const amount = this.lastPurchases.reduce((accumulator, currentValue) => { - if (currentValue.transactionType !== 'PayFromWallet') return 0; - return accumulator + currentValue.transactionSum; + if (currentValue.transactionType !== 'PayFromWallet') return accumulator; + return accumulator + currentValue.orderSum!; }, 0); - return amount * -1 + return amount * 1 } }; @@ -85,12 +85,14 @@ export class GuestCardComponent implements OnInit { 'PayFromWallet', 'CancelPayFromWallet', 'RefillWallet', + 'RefillWalletFromOrder' ].includes(purchase.transactionType || '') ); - this.lastPurchase = value[this.customerInfo?.id].filter( + this.lastPurchase = this.purchases.filter( (purchase: Purchase) => [ 'PayFromWallet', + 'RefillWalletFromOrder' ].includes(purchase.transactionType || '') )[0] @@ -114,7 +116,7 @@ export class GuestCardComponent implements OnInit { } deleteToken(): void { - this.cookiesService.deleteCookie('token'); + this.cookiesService.logout(); } logout() { diff --git a/angular/src/app/presentation-options/default-option/components/accordion/accordion.component.html b/angular/src/app/presentation-options/default-option/components/accordion/accordion.component.html new file mode 100644 index 0000000..3af52b9 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/accordion/accordion.component.html @@ -0,0 +1,7 @@ +
+ + +
+ +
+
diff --git a/angular/src/app/presentation-options/default-option/components/accordion/accordion.component.scss b/angular/src/app/presentation-options/default-option/components/accordion/accordion.component.scss new file mode 100644 index 0000000..68f274d --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/accordion/accordion.component.scss @@ -0,0 +1,85 @@ +:host { + width: 100%; +} + +.tab { + position: relative; + margin-bottom: 1px; + width: 100%; + color: #fff; + overflow: hidden; + border-bottom: solid 1px #bdbdbd; +} +.tab input { + position: absolute; + opacity: 0; + z-index: -1; +} +.tab label { + display: block; + padding: 20px 26px; + position: relative; + background: #ffffff00; + font-weight: 400; + font-size: 12px; + line-height: 14px; + letter-spacing: -0.5px; + cursor: pointer; +} +// .tab label:before { +// display: block; +// content: " "; +// padding-top: 5px; +// } +.tab-content { + max-height: 0; + overflow: hidden; + background: #292929; + -webkit-transition: all 0.35s; + -o-transition: all 0.35s; + transition: all 0.35s; + border-top: solid 1px #bdbdbd; + font-style: normal; + font-weight: 400; + font-size: 12px; + + p { + margin: 0; + } +} +.tab-content p { + margin: 1em; +} +/* :checked */ +.tab input:checked ~ .tab-content { + padding: 16px 26px; + max-height: 100vh; +} + +/* Icon */ +.tab label::after { + position: absolute; + right: 0; + top: 0; + line-height: 3; + text-align: center; + -webkit-transition: all 0.35s; + -o-transition: all 0.35s; + transition: all 0.35s; + height: 100%; + margin-right: 12px; + display: flex; + align-items: center; +} +.tab input[type="checkbox"] + label::after { + content: "+"; +} +.tab input[type="radio"] + label::after { + content: "\25BC"; +} +.tab input[type="checkbox"]:checked + label::after { + transform: rotate(315deg); +} +.tab input[type="radio"]:checked + label::after { + transform: rotateX(180deg); +} diff --git a/angular/src/app/presentation-options/default-option/components/accordion/accordion.component.spec.ts b/angular/src/app/presentation-options/default-option/components/accordion/accordion.component.spec.ts new file mode 100644 index 0000000..73cc3e3 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/accordion/accordion.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AccordionComponent } from './accordion.component'; + +describe('AccordionComponent', () => { + let component: AccordionComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AccordionComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AccordionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/default-option/components/accordion/accordion.component.ts b/angular/src/app/presentation-options/default-option/components/accordion/accordion.component.ts new file mode 100644 index 0000000..1958e5e --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/accordion/accordion.component.ts @@ -0,0 +1,23 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { v4 as uuidv4 } from 'uuid'; + +export interface IAccordionData { + header: string; + body: string; +} + +@Component({ + selector: 'app-accordion[header]', + templateUrl: './accordion.component.html', + styleUrls: ['./accordion.component.scss'] +}) +export class AccordionComponent implements OnInit { + @Input() header!: string + public guid: string = uuidv4() + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/angular/src/app/presentation-options/default-option/components/footer/footer.component.html b/angular/src/app/presentation-options/default-option/components/footer/footer.component.html new file mode 100644 index 0000000..a3bb425 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/footer/footer.component.html @@ -0,0 +1,4 @@ +Логотип +

Горячая линия

+8 (800) 333-41-30 + diff --git a/angular/src/app/presentation-options/default-option/components/footer/footer.component.scss b/angular/src/app/presentation-options/default-option/components/footer/footer.component.scss new file mode 100644 index 0000000..04b6b18 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/footer/footer.component.scss @@ -0,0 +1,27 @@ +:host { + display: flex; + flex-direction: column; + align-items: center; + gap: 20px; + padding-bottom: 100px; + h3 { + margin: 0; + font-style: normal; + font-weight: 400; + font-size: 15px; + line-height: 20px; + text-align: center; + letter-spacing: -0.24px; + color: #6a737c; + } + .phone-number { + font-style: normal; + font-weight: 700; + font-size: 17px; + line-height: 22px; + text-align: center; + letter-spacing: -0.408px; + color: #d9d9d9; + text-decoration: none; + } +} diff --git a/angular/src/app/presentation-options/default-option/components/footer/footer.component.spec.ts b/angular/src/app/presentation-options/default-option/components/footer/footer.component.spec.ts new file mode 100644 index 0000000..953b22c --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/footer/footer.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FooterComponent } from './footer.component'; + +describe('FooterComponent', () => { + let component: FooterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FooterComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(FooterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/default-option/components/footer/footer.component.ts b/angular/src/app/presentation-options/default-option/components/footer/footer.component.ts new file mode 100644 index 0000000..c7a7ec5 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/footer/footer.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-footer', + templateUrl: './footer.component.html', + styleUrls: ['./footer.component.scss'] +}) +export class FooterComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/angular/src/app/presentation-options/default-option/components/index/index.component.html b/angular/src/app/presentation-options/default-option/components/index/index.component.html new file mode 100644 index 0000000..4cca73f --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/index/index.component.html @@ -0,0 +1,2 @@ + + diff --git a/angular/src/app/presentation-options/default-option/components/index/index.component.scss b/angular/src/app/presentation-options/default-option/components/index/index.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/angular/src/app/presentation-options/default-option/components/index/index.component.spec.ts b/angular/src/app/presentation-options/default-option/components/index/index.component.spec.ts new file mode 100644 index 0000000..5e5ef12 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/index/index.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { IndexComponent } from './index.component'; + +describe('IndexComponent', () => { + let component: IndexComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ IndexComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(IndexComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/default-option/components/index/index.component.ts b/angular/src/app/presentation-options/default-option/components/index/index.component.ts new file mode 100644 index 0000000..65085f2 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/index/index.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-index', + templateUrl: './index.component.html', + styleUrls: ['./index.component.scss'] +}) +export class IndexComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.html b/angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.html new file mode 100644 index 0000000..5543fb8 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.html @@ -0,0 +1,25 @@ +

Пригласи друзей!

+
+

+ Пригласи друзей зарегистрироваться в приложении по твоему уникальному коду и + получи бонусы, когда они совершат первую покупку. +

+ +
+ + + + diff --git a/angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.scss b/angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.scss new file mode 100644 index 0000000..0ff76c3 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.scss @@ -0,0 +1,33 @@ +:host { + padding: 16px; + h2 { + font-style: normal; + font-weight: 700; + font-size: 17px; + line-height: 22px; + letter-spacing: -0.408px; + } + + & .container { + width: 100%; + display: flex; + flex-direction: row; + gap: 16px; + + p { + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + color: #ffffff; + } + + .share { + width: 72px; + height: 48px; + background: #28af49; + padding: 8px 26px; + border: none; + } + } +} diff --git a/angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.spec.ts b/angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.spec.ts new file mode 100644 index 0000000..2c9c394 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { InviteFriendsComponent } from './invite-friends.component'; + +describe('InviteFriendsComponent', () => { + let component: InviteFriendsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ InviteFriendsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(InviteFriendsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.ts b/angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.ts new file mode 100644 index 0000000..b05b189 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/invite-friends/invite-friends.component.ts @@ -0,0 +1,54 @@ +import { Component, OnInit } from '@angular/core'; +import { MessageService } from 'primeng/api'; +import { lastValueFrom } from 'rxjs'; +import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; +import { environment } from 'src/environments/environment'; + +@Component({ + selector: 'app-invite-friends', + templateUrl: './invite-friends.component.html', + styleUrls: ['./invite-friends.component.scss'] +}) +export class InviteFriendsComponent implements OnInit { + public refUrl: string = `${environment.defaultUrl}/?refUserId=` + public loading: boolean = true; + private shareData: ShareData = { + title: '' + } + + constructor( + private jsonrpc: JsonrpcService, + private messageService: MessageService + ) { } + + async ngOnInit() { + const accountData = (await lastValueFrom( + this.jsonrpc + .rpc( + { + method: 'getTokenData', + params: [], + }, + RpcService.authService, + true + ) + )).data + + this.refUrl += accountData.user_id + this.loading = false + } + + share() { + if (navigator.share) { + navigator.share({ + title: document.title, + text: "Coffee Like", + url: this.refUrl + }) + .then(() => console.log('Successful share')) + .catch((error) => { + console.log('Error sharing:', error) + }); + } + } +} diff --git a/angular/src/app/presentation-options/default-option/components/last-order/last-order.component.html b/angular/src/app/presentation-options/default-option/components/last-order/last-order.component.html new file mode 100644 index 0000000..a5e4b7e --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/last-order/last-order.component.html @@ -0,0 +1,31 @@ +

Ваш предыдущий заказ

+
+

Дата: + {{(lastOrder?.transactionCreateDate | date:'dd.MM.yyyyг.') || 'Данные не найдены'}} + + + +

+

На сумму: + {{lastOrder?.orderSum ? lastOrder?.orderSum + ' ₽' : 'Данные не найдены'}} + + + +

+
+ + + +

+ Списание бонусов возможно на любые категории. Бонусами можно оплатить 100% + суммы покупки. Бонусы начисляются только на напитки с учётом добавок. + Неиспользованные бонусы сгорают в течение 90 дней. +

+ + + + \ No newline at end of file diff --git a/angular/src/app/presentation-options/default-option/components/last-order/last-order.component.scss b/angular/src/app/presentation-options/default-option/components/last-order/last-order.component.scss new file mode 100644 index 0000000..4c5a2c0 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/last-order/last-order.component.scss @@ -0,0 +1,51 @@ +:host { + padding: 24px 16px 56px; + + & > h2 { + font-style: normal; + font-weight: 700; + font-size: 15px; + line-height: 20px; + letter-spacing: -0.24px; + } + + & > .info-order { + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + span { + color: #828282; + } + .flex { + display: flex; + align-items: center; + flex-direction: row; + gap: 8px; + } + } + + .evaluate-order { + margin: 24px 0; + width: 100%; + padding: 12px; + text-align: center; + border: 2px solid #28af49; + border-radius: 6px; + font-style: normal; + font-weight: 700; + font-size: 17px; + line-height: 22px; + letter-spacing: -0.408px; + background-color: transparent; + color: #28af49; + } + + .info { + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + color: rgba(255, 255, 255, 0.5); + } +} diff --git a/angular/src/app/presentation-options/default-option/components/last-order/last-order.component.spec.ts b/angular/src/app/presentation-options/default-option/components/last-order/last-order.component.spec.ts new file mode 100644 index 0000000..101c8ed --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/last-order/last-order.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LastOrderComponent } from './last-order.component'; + +describe('LastOrderComponent', () => { + let component: LastOrderComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ LastOrderComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LastOrderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/default-option/components/last-order/last-order.component.ts b/angular/src/app/presentation-options/default-option/components/last-order/last-order.component.ts new file mode 100644 index 0000000..0117951 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/last-order/last-order.component.ts @@ -0,0 +1,18 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { Purchase } from 'src/app/interface/data'; + +@Component({ + selector: 'app-last-order[lastOrder]', + templateUrl: './last-order.component.html', + styleUrls: ['./last-order.component.scss'] +}) +export class LastOrderComponent implements OnInit { + @Input() lastOrder!: Purchase; + @Input() loading!: boolean; + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/angular/src/app/presentation-options/default-option/components/navbar/navbar.component.html b/angular/src/app/presentation-options/default-option/components/navbar/navbar.component.html new file mode 100644 index 0000000..e3df091 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/navbar/navbar.component.html @@ -0,0 +1,5 @@ +
+ +

{{title}}

+
+
diff --git a/angular/src/app/presentation-options/default-option/components/navbar/navbar.component.scss b/angular/src/app/presentation-options/default-option/components/navbar/navbar.component.scss new file mode 100644 index 0000000..dcd7a52 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/navbar/navbar.component.scss @@ -0,0 +1,33 @@ +:host { + width: 100%; +} + +.container { + box-sizing: border-box; + padding: 12px 16px; + width: 100%; + background: #231f20; + box-shadow: 0px 8px 16px rgba(17, 17, 17, 0.5); + color: #fff; + display: flex; + align-items: center; + justify-content: space-between; + .back-arrow { + font-size: 16px; + display: flex; + align-items: center; + justify-content: center; + } + .plug { + height: 24px; + width: 24px; + visibility: hidden; + } + .title { + font-family: "Gotham Pro", sans-serif; + font-weight: 700; + font-size: 17px; + line-height: 22px; + margin: 0; + } +} diff --git a/angular/src/app/presentation-options/default-option/components/navbar/navbar.component.spec.ts b/angular/src/app/presentation-options/default-option/components/navbar/navbar.component.spec.ts new file mode 100644 index 0000000..505cc2f --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/navbar/navbar.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NavbarComponent } from './navbar.component'; + +describe('NavbarComponent', () => { + let component: NavbarComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ NavbarComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(NavbarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/default-option/components/navbar/navbar.component.ts b/angular/src/app/presentation-options/default-option/components/navbar/navbar.component.ts new file mode 100644 index 0000000..f0300d0 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/navbar/navbar.component.ts @@ -0,0 +1,21 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +@Component({ + selector: 'app-navbar[title]', + templateUrl: './navbar.component.html', + styleUrls: ['./navbar.component.scss'] +}) +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/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.html b/angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.html new file mode 100644 index 0000000..98fc383 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.html @@ -0,0 +1,3 @@ + + + diff --git a/angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.scss b/angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.scss new file mode 100644 index 0000000..6cd7512 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.scss @@ -0,0 +1,15 @@ +:host { + display: flex; + flex-direction: row; + gap: 16px; + justify-content: space-between; + a { + width: 48px; + height: 48px; + border-radius: 100%; + background: #333333; + display: flex; + align-items: center; + justify-content: center; + } +} \ No newline at end of file diff --git a/angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.spec.ts b/angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.spec.ts new file mode 100644 index 0000000..b8cd680 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SocialMediaButtonsComponent } from './social-media-buttons.component'; + +describe('SocialMediaButtonsComponent', () => { + let component: SocialMediaButtonsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ SocialMediaButtonsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SocialMediaButtonsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.ts b/angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.ts new file mode 100644 index 0000000..2c659b0 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/components/social-media-buttons/social-media-buttons.component.ts @@ -0,0 +1,38 @@ +import { Component, OnInit } from '@angular/core'; + +export interface ISocialMediaLink { + url: string; + imgUrl: string; + label: string; +} + +@Component({ + selector: 'app-social-media-buttons', + templateUrl: './social-media-buttons.component.html', + styleUrls: ['./social-media-buttons.component.scss'] +}) +export class SocialMediaButtonsComponent implements OnInit { + public links: ISocialMediaLink[] = [ + { + label: 'Инстаграм', + url: 'https://www.instagram.com/', + imgUrl: '/assets/social-media-icons/instagram.svg' + }, + { + label: 'ВК', + url: 'https://vk.com/coffeelike_com', + imgUrl: '/assets/social-media-icons/vk.svg' + }, + { + label: 'Youtube', + url: 'https://www.youtube.com/c/coffeelikeru', + imgUrl: '/assets/social-media-icons/youtube.svg' + } + ] + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/angular/src/app/presentation-options/default-option/default-option-routing.module.ts b/angular/src/app/presentation-options/default-option/default-option-routing.module.ts new file mode 100644 index 0000000..27b517c --- /dev/null +++ b/angular/src/app/presentation-options/default-option/default-option-routing.module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { IndexComponent } from './components/index/index.component'; +import { GuestCardComponent } from './pages/guest-card/guest-card.component'; +import { AuthGuard } from 'src/app/guards/auth-guard.guard'; +import { LoginComponent } from './pages/login/login.component'; + +const routes: Routes = [ + { + path: '', + component: GuestCardComponent, + canActivate: [AuthGuard], + }, + { + path: 'login', + component: LoginComponent + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class DefaultOptionRoutingModule {} diff --git a/angular/src/app/presentation-options/default-option/default-option.module.ts b/angular/src/app/presentation-options/default-option/default-option.module.ts new file mode 100644 index 0000000..d0541ee --- /dev/null +++ b/angular/src/app/presentation-options/default-option/default-option.module.ts @@ -0,0 +1,49 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { DefaultOptionRoutingModule } from './default-option-routing.module'; +import { IndexComponent } from './components/index/index.component'; +import { FooterComponent } from './components/footer/footer.component'; +import { SocialMediaButtonsComponent } from './components/social-media-buttons/social-media-buttons.component'; +import { NavbarComponent } from './components/navbar/navbar.component'; +import { MatIconModule } from '@angular/material/icon'; +import { GuestCardComponent } from './pages/guest-card/guest-card.component'; +import { QrCodeModule } from 'ng-qrcode'; +import { AccordionComponent } from './components/accordion/accordion.component'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { LastOrderComponent } from './components/last-order/last-order.component'; +import { InviteFriendsComponent } from './components/invite-friends/invite-friends.component'; +import { LoginComponent } from './pages/login/login.component'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { NgxMatIntlTelInputComponent } from 'ngx-mat-intl-tel-input'; +import { MatInputModule } from '@angular/material/input'; +import { DirectivesModule } from 'src/app/directives/directives.module'; + +@NgModule({ + declarations: [ + IndexComponent, + FooterComponent, + SocialMediaButtonsComponent, + NavbarComponent, + GuestCardComponent, + AccordionComponent, + LastOrderComponent, + InviteFriendsComponent, + LoginComponent + ], + imports: [ + CommonModule, + DefaultOptionRoutingModule, + MatIconModule, + QrCodeModule, + MatProgressSpinnerModule, + FormsModule, + MatFormFieldModule, + NgxMatIntlTelInputComponent, + ReactiveFormsModule, + MatInputModule, + DirectivesModule + ], + bootstrap: [IndexComponent], +}) +export class DefaultOptionModule {} diff --git a/angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.html b/angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.html new file mode 100644 index 0000000..e2ccabe --- /dev/null +++ b/angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.html @@ -0,0 +1,198 @@ + + +
+
+ + + + + + +
+
+ Текущий баланс бонусов: + + {{ loyaltyProgram.getBalanceAmount(customerInfo?.walletBalances) }} + бонусов +
+ + +

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

+

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

+

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

+
+ +

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

+

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

+

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

+

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

+

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

+
+ +

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

+ +
    + Возврат покупки, за которую бонусы были начислены: +
  • + В случае, если бонусов на счету достаточно для списания, бонусы + списываются в полном ранее начисленном за возвращаемый товар объеме. +
  • +
  • + В случае, если бонусов на счету недостаточно, формируется минусовой + баланс. +
  • +
+ +
    + Возврат покупки, которая была оплачена бонусами: +
  • + В случае предъявления Участником кассового или товарного чека, сумма + бонусов, списанная для оплаты возвращаемого товара, зачисляется на счет + участника. +
  • +
  • + В случае возврата товара с применением оплаты бонусами, клиенту + возвращается денежная сумма в размере, внесенном Участником в оплату + товара при покупке, за вычетом суммы, оплаченной бонусами. +
  • +
+
+ +
+ Сумма ваших покупок за период с + {{ loyaltyProgram.purchaseData.currentPeriod[0].format("DD.MM.yyyyг.") }} - + {{ loyaltyProgram.purchaseData.currentAmount }} руб. + + + +
+ +

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

+ + + +
    + Уровень {{ index + 1 }} +
  • + Сумма покупок за предыдущий период {{ item.start }}-{{ item.end }} + руб. +
  • +
  • Начисляемый бонус {{ item.percent }}% от суммы покупки
  • +
+
+
+ +
    + Уровень {{ index + 1 }} +
  • Сумма покупок за предыдущий период — от {{ item.start }} руб.
  • +
  • Начисляемый бонус, в % от суммы покупки - {{ item.percent }}%
  • +
+
+
+
+ +
+ + +

+ До следующего уровня за период с + {{ loyaltyProgram.purchaseData.currentPeriod[0].format("DD.MM.yyyyг") }} по + {{ loyaltyProgram.purchaseData.currentPeriod[1].format("DD.MM.yyyyг") }} + осталось совершить покупки на {{ currentLvlPeriod.end - (loyaltyProgram.purchaseData.currentAmount || 0) + 1 }} рублей +

+ +
+ +

+ У Вас последний уровень бонусной программы. Процент начисляемых бонусов: {{currentLvlPeriod.percent}}% +

+
+
+ + + + +

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

+
+
+ +
+ +
+
+ Скачай приложение +
+ Подробнее о правилах
+ Программы лояльности
+
+ + + + diff --git a/angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.scss b/angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.scss new file mode 100644 index 0000000..f3aab9b --- /dev/null +++ b/angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.scss @@ -0,0 +1,199 @@ +:host { + .guest-card { + display: flex; + flex-direction: column; + align-items: center; + padding: 8px 0 0; + max-width: 600px; + margin: 0 auto; + + &__qr { + padding: 10px; + width: fit-content; + background-image: linear-gradient( + #fff 33%, + transparent 0px, + transparent 67%, + #fff 0px + ), + linear-gradient( + 90deg, + #ffe 33%, + transparent 0px, + transparent 66%, + #fff 0px + ), + linear-gradient(#fff 33%, transparent 0px, transparent 67%, #fff 0), + linear-gradient(90deg, #fff 33%, transparent 0, transparent 66%, #fff 0); + background-size: 1px 100%, 100% 1px, 1px 100%, 100% 1px; + background-position: 0 0, 0 0, 100% 100%, 100% 100%; + background-repeat: no-repeat, no-repeat, no-repeat, no-repeat; + cursor: pointer; + } + + &__user-description { + margin: 18px 0 0; + padding: 14px 24px; + width: 100%; + text-align: left; + border-top: 1px solid #fff; + border-bottom: 1px solid #fff; + // font-family: "Goldman"; + font-style: normal; + font-weight: 400; + font-size: 16px; + line-height: 19px; + letter-spacing: -0.5px; + + span { + color: #f2994a; + } + } + + &__purchases-description { + margin: 0; + padding: 14px 24px; + width: 100%; + text-align: left; + border-bottom: 1px solid #fff; + // font-family: "Goldman"; + font-style: normal; + font-weight: 400; + font-size: 16px; + line-height: 19px; + letter-spacing: -0.5px; + grid-template-columns: calc(100% - 24px) 24px; + + span { + color: #219653; + } + } + + &__level-info { + padding: 36px; + display: flex; + flex-direction: column; + align-items: center; + + h2 { + font-style: normal; + font-weight: 700; + font-size: 17px; + line-height: 22px; + text-align: center; + letter-spacing: -0.408px; + } + + input[type="range"] { + -webkit-appearance: none; + width: 100%; + height: 6px; + border-radius: 5px; + display: block; + position: relative; + background: #231f20; + box-shadow: 0px 0px 3px #f2c94c59; + background-image: linear-gradient(#f2c94c, #f2c94c); + background-size: 70% 100%; + background-repeat: no-repeat; + + &::before, + &::after { + content: " "; + display: block; + width: 16px; + height: 16px; + border-radius: 100%; + position: absolute; + top: -5px; + } + + &::before { + background-color: #f2c94c; + left: 0px; + } + + &::after { + background-color: #f2c94c; + right: 0px; + } + + &::-webkit-slider-thumb { + -webkit-appearance: none; + background: #f2c94c; + width: 16px; + height: 16px; + border-radius: 100%; + display: none; + } + + &::-ms-thumb { + -webkit-appearance: none; + height: 20px; + width: 20px; + border-radius: 50%; + background: #f2c94c; + cursor: ew-resize; + box-shadow: 0 0 2px 0 #555; + transition: background 0.3s ease-in-out; + } + + &::-moz-range-thumb { + -webkit-appearance: none; + height: 20px; + width: 20px; + border-radius: 50%; + background: #f2c94c; + cursor: ew-resize; + box-shadow: 0 0 2px 0 #555; + transition: background 0.3s ease-in-out; + } + } + + & > .show-more { + margin-top: 42px; + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + text-align: center; + a { + text-decoration: none; + color: #28af49; + } + } + } + + &__download-app { + width: 100%; + position: relative; + margin-top: 32px; + display: flex; + justify-content: flex-end; + img { + width: 100%; + max-width: calc(100% - 16px); + } + } + + &__loyalty-program { + text-align: center; + color: rgba(255, 255, 255, 0.5); + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + text-decoration: none; + margin: 17px 0 22px; + } + } +} + +app-accordion { + ul { + li { + list-style-type: disc; + margin-left: 16px; + } + } +} diff --git a/angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.spec.ts b/angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.spec.ts new file mode 100644 index 0000000..f9a4d88 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { GuestCardComponent } from './guest-card.component'; + +describe('GuestCardComponent', () => { + let component: GuestCardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ GuestCardComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(GuestCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.ts b/angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.ts new file mode 100644 index 0000000..6f9da3d --- /dev/null +++ b/angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.ts @@ -0,0 +1,99 @@ +import { Component, OnInit } from '@angular/core'; +import { MatBottomSheet } from '@angular/material/bottom-sheet'; +import { Router } from '@angular/router'; +import { Observable } from 'rxjs'; +import { ExitComponent } from 'src/app/components/exit/exit.component'; +import { CookiesService } from 'src/app/services/cookies.service'; +import { WpJsonService } from 'src/app/services/wp-json.service'; +import { environment } from 'src/environments/environment'; +import moment from 'moment'; +import { Moment, Purchase, lvlPeriod } from 'src/app/interface/data'; +import { lvlPeriods } from 'src/app/app.constants'; +import { LoyaltyProgramService } from 'src/app/services/loyalty-program.service'; + + +@Component({ + selector: 'app-guest-card', + templateUrl: './guest-card.component.html', + styleUrls: ['./guest-card.component.scss'], +}) +export class GuestCardComponent implements OnInit { + public qrCodeSize: number = 85; + private isQrCodeClicked: boolean = false; + public customerInfo!: any; + public purchases!: Purchase[]; + public lastPurchase!: Purchase; + + public lvlPeriods: lvlPeriod[] = lvlPeriods; + public currentLvlPeriod!: lvlPeriod; + + constructor( + private _bottomSheet: MatBottomSheet, + public cookiesService: CookiesService, + private router: Router, + private wpJsonService: WpJsonService, + public loyaltyProgram: LoyaltyProgramService + ) {} + + ngOnInit(): void { + const token = this.cookiesService.getItem('token'); + this.wpJsonService + .getCustomerInfo( + environment.systemId, + token || '', + environment.icardProxy + ) + .subscribe({ + next: (value) => { + this.customerInfo = value.customer_info; + this.cookiesService.setCookie('phone-number', this.customerInfo.phone.substr(2)) + this.getPurchases().subscribe((value) => { + this.purchases = this.loyaltyProgram.filterPurchases(value[this.customerInfo?.id]) + this.lastPurchase = this.loyaltyProgram.getLastPurchase(this.purchases) + + this.loyaltyProgram.setLastPurchases(this.purchases) + this.loyaltyProgram.setCurrentPurchases(this.purchases) + + const currentAmount = this.loyaltyProgram.purchaseData.currentAmount || 0 + this.currentLvlPeriod = this.lvlPeriods.find((item) => item.start <= currentAmount && currentAmount <= (item.end || Infinity))! + this.loyaltyProgram.purchaseData.$loading = false; + }); + }, + }); + } + + qrCodeClick() { + this.isQrCodeClicked = !this.isQrCodeClicked; + this.qrCodeSize = this.isQrCodeClicked ? 180 : 85; + } + + deleteToken(): void { + this.cookiesService.logout(); + } + + 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().subtract(1, 'months').startOf('month'), + end: Date | Moment = moment() + ): Observable { + const token = this.cookiesService.getItem('token'); + const delta = moment(end).diff(moment(start), 'days'); + return this.wpJsonService.getTransactions( + environment.systemId, + token ?? '', + environment.icardProxy + ); + } + +} diff --git a/angular/src/app/presentation-options/default-option/pages/login/login.component.html b/angular/src/app/presentation-options/default-option/pages/login/login.component.html new file mode 100644 index 0000000..2069964 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/pages/login/login.component.html @@ -0,0 +1,107 @@ + +

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

+

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

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

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

+ +
+ + +

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

+
+
+ + + + +
+ +
+

+ Не пришло SMS?
+ + Отправим повторно через {{timeLeft}}с + + + Отправить повторно + +

+
diff --git a/angular/src/app/presentation-options/default-option/pages/login/login.component.scss b/angular/src/app/presentation-options/default-option/pages/login/login.component.scss new file mode 100644 index 0000000..765c9a7 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/pages/login/login.component.scss @@ -0,0 +1,214 @@ +:host { + padding-top: 48px; + display: flex; + flex-direction: column; + align-items: center; + max-width: 600px; + margin: 0 auto 52px; + + h1 { + margin-top: 20px; + width: 302px; + font-style: normal; + font-weight: 700; + font-size: 17px; + line-height: 22px; + text-align: center; + letter-spacing: -0.408px; + } + + h2 { + margin-top: 44px; + font-style: normal; + font-weight: 700; + font-size: 20px; + line-height: 24px; + text-align: center; + letter-spacing: 0.38px; + } + + .description { + width: 180px; + font-style: normal; + font-weight: 400; + font-size: 15px; + line-height: 20px; + text-align: center; + letter-spacing: -0.24px; + margin-top: 16px; + } + + form { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 35px; + + .offer { + margin-top: 10px; + padding: 0 16px; + font-family: "Gowun Dodum"; + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 17px; + text-align: center; + span { + color: #13a538; + } + } + + .input-container { + position: relative; + width: 100%; + + label { + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + color: #6a737c; + text-align: left; + position: absolute; + top: 10px; + left: 16px; + } + + 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; + } + } + + button { + height: 46px; + width: calc(100% - 32px); + padding: 10px 24px; + background: #28af49; + border-radius: 6px; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + border: none; + letter-spacing: -0.408px; + color: #ffffff; + font-style: normal; + 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; + // } + .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; + } + } + } + + .resend-code { + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + text-align: center; + margin-top: 23px; + .resend { + cursor: pointer; + } + } +} + +mat-form-field { + width: 100%; +} diff --git a/angular/src/app/presentation-options/default-option/pages/login/login.component.spec.ts b/angular/src/app/presentation-options/default-option/pages/login/login.component.spec.ts new file mode 100644 index 0000000..10eca24 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/pages/login/login.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginComponent } from './login.component'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ LoginComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/default-option/pages/login/login.component.ts b/angular/src/app/presentation-options/default-option/pages/login/login.component.ts new file mode 100644 index 0000000..0a60904 --- /dev/null +++ b/angular/src/app/presentation-options/default-option/pages/login/login.component.ts @@ -0,0 +1,167 @@ +import { + AfterViewInit, + Component, + EventEmitter, + HostListener, + OnInit +} from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { MatSnackBar } from '@angular/material/snack-bar'; +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, AfterViewInit { + public isShowNumber: boolean = true; + public phoneForm = new FormGroup({ + name: new FormControl('', []), + 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']; + timeLeft: number = 0; + + constructor( + private cookiesService: CookiesService, + private router: Router, + private jsonrpc: JsonrpcService, + private messageService: MessageService, + private _snackBar: MatSnackBar + ) {} + + 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; + this.isShowNumber = false; + if (this.timeLeft) { + this.messageService.add({ + severity: 'custom', + summary: `Отправить повторно можно через ${this.timeLeft}с`, + }); + return; + } + this.jsonrpc.rpc({ + method: 'sendVerifyByPhone', + params: [data.phone] + }, RpcService.authService, false).subscribe({ + next: (result) => { + if (result.code !== 0) { + this._snackBar.open('Произошла ошибка! Попробуйте позже', '', { + duration: 3000 + }) + } + if (result.code === 0) { + this.timeLeft = 60; + const interval = setInterval(() => { + if(this.timeLeft > 0) { + this.timeLeft--; + } else { + clearInterval(interval); + } + },1000) + } + this.isShowNumber = false; + }, + error: (error) => { + console.error('Error: ', error); + + } + } + ); + setTimeout(() => { + this.inputFocusEmitter.emit(`#${this.inputIds[0]}`); + }, 0) + } + + submitCode() { + const data = this.codeForm.value; + this.jsonrpc.rpc({ + method: 'getTokenByPhone', + params: [this.phoneForm.value.phone, Object.values(data).join('')] + }, RpcService.authService, false).subscribe({ + next: (result) => { + if (result.code === 0) { + this.cookiesService.setCookie('token', result?.data?.token); + this.router.navigate(['/'], { + queryParams: { + token: result?.data?.token + }, + }); + // this.phoneConfirmed.emit(null); + } else if (result.code === 230) { + this._snackBar.open('Неверный код!', '', { + duration: 3000 + }) + // 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/presentation-options/first-option/components/accordion/accordion.component.html b/angular/src/app/presentation-options/first-option/components/accordion/accordion.component.html new file mode 100644 index 0000000..3af52b9 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/accordion/accordion.component.html @@ -0,0 +1,7 @@ +
+ + +
+ +
+
diff --git a/angular/src/app/presentation-options/first-option/components/accordion/accordion.component.scss b/angular/src/app/presentation-options/first-option/components/accordion/accordion.component.scss new file mode 100644 index 0000000..68f274d --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/accordion/accordion.component.scss @@ -0,0 +1,85 @@ +:host { + width: 100%; +} + +.tab { + position: relative; + margin-bottom: 1px; + width: 100%; + color: #fff; + overflow: hidden; + border-bottom: solid 1px #bdbdbd; +} +.tab input { + position: absolute; + opacity: 0; + z-index: -1; +} +.tab label { + display: block; + padding: 20px 26px; + position: relative; + background: #ffffff00; + font-weight: 400; + font-size: 12px; + line-height: 14px; + letter-spacing: -0.5px; + cursor: pointer; +} +// .tab label:before { +// display: block; +// content: " "; +// padding-top: 5px; +// } +.tab-content { + max-height: 0; + overflow: hidden; + background: #292929; + -webkit-transition: all 0.35s; + -o-transition: all 0.35s; + transition: all 0.35s; + border-top: solid 1px #bdbdbd; + font-style: normal; + font-weight: 400; + font-size: 12px; + + p { + margin: 0; + } +} +.tab-content p { + margin: 1em; +} +/* :checked */ +.tab input:checked ~ .tab-content { + padding: 16px 26px; + max-height: 100vh; +} + +/* Icon */ +.tab label::after { + position: absolute; + right: 0; + top: 0; + line-height: 3; + text-align: center; + -webkit-transition: all 0.35s; + -o-transition: all 0.35s; + transition: all 0.35s; + height: 100%; + margin-right: 12px; + display: flex; + align-items: center; +} +.tab input[type="checkbox"] + label::after { + content: "+"; +} +.tab input[type="radio"] + label::after { + content: "\25BC"; +} +.tab input[type="checkbox"]:checked + label::after { + transform: rotate(315deg); +} +.tab input[type="radio"]:checked + label::after { + transform: rotateX(180deg); +} diff --git a/angular/src/app/presentation-options/first-option/components/accordion/accordion.component.spec.ts b/angular/src/app/presentation-options/first-option/components/accordion/accordion.component.spec.ts new file mode 100644 index 0000000..73cc3e3 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/accordion/accordion.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AccordionComponent } from './accordion.component'; + +describe('AccordionComponent', () => { + let component: AccordionComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AccordionComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AccordionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/first-option/components/accordion/accordion.component.ts b/angular/src/app/presentation-options/first-option/components/accordion/accordion.component.ts new file mode 100644 index 0000000..1958e5e --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/accordion/accordion.component.ts @@ -0,0 +1,23 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { v4 as uuidv4 } from 'uuid'; + +export interface IAccordionData { + header: string; + body: string; +} + +@Component({ + selector: 'app-accordion[header]', + templateUrl: './accordion.component.html', + styleUrls: ['./accordion.component.scss'] +}) +export class AccordionComponent implements OnInit { + @Input() header!: string + public guid: string = uuidv4() + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/angular/src/app/presentation-options/first-option/components/footer/footer.component.html b/angular/src/app/presentation-options/first-option/components/footer/footer.component.html new file mode 100644 index 0000000..a3bb425 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/footer/footer.component.html @@ -0,0 +1,4 @@ +Логотип +

Горячая линия

+8 (800) 333-41-30 + diff --git a/angular/src/app/presentation-options/first-option/components/footer/footer.component.scss b/angular/src/app/presentation-options/first-option/components/footer/footer.component.scss new file mode 100644 index 0000000..04b6b18 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/footer/footer.component.scss @@ -0,0 +1,27 @@ +:host { + display: flex; + flex-direction: column; + align-items: center; + gap: 20px; + padding-bottom: 100px; + h3 { + margin: 0; + font-style: normal; + font-weight: 400; + font-size: 15px; + line-height: 20px; + text-align: center; + letter-spacing: -0.24px; + color: #6a737c; + } + .phone-number { + font-style: normal; + font-weight: 700; + font-size: 17px; + line-height: 22px; + text-align: center; + letter-spacing: -0.408px; + color: #d9d9d9; + text-decoration: none; + } +} diff --git a/angular/src/app/presentation-options/first-option/components/footer/footer.component.spec.ts b/angular/src/app/presentation-options/first-option/components/footer/footer.component.spec.ts new file mode 100644 index 0000000..953b22c --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/footer/footer.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FooterComponent } from './footer.component'; + +describe('FooterComponent', () => { + let component: FooterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FooterComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(FooterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/first-option/components/footer/footer.component.ts b/angular/src/app/presentation-options/first-option/components/footer/footer.component.ts new file mode 100644 index 0000000..c7a7ec5 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/footer/footer.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-footer', + templateUrl: './footer.component.html', + styleUrls: ['./footer.component.scss'] +}) +export class FooterComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/angular/src/app/presentation-options/first-option/components/index/index.component.html b/angular/src/app/presentation-options/first-option/components/index/index.component.html new file mode 100644 index 0000000..4cca73f --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/index/index.component.html @@ -0,0 +1,2 @@ + + diff --git a/angular/src/app/presentation-options/first-option/components/index/index.component.scss b/angular/src/app/presentation-options/first-option/components/index/index.component.scss new file mode 100644 index 0000000..8dfb832 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/index/index.component.scss @@ -0,0 +1,3 @@ +* { + color: red; +} \ No newline at end of file diff --git a/angular/src/app/presentation-options/first-option/components/index/index.component.spec.ts b/angular/src/app/presentation-options/first-option/components/index/index.component.spec.ts new file mode 100644 index 0000000..5e5ef12 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/index/index.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { IndexComponent } from './index.component'; + +describe('IndexComponent', () => { + let component: IndexComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ IndexComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(IndexComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/first-option/components/index/index.component.ts b/angular/src/app/presentation-options/first-option/components/index/index.component.ts new file mode 100644 index 0000000..65085f2 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/index/index.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-index', + templateUrl: './index.component.html', + styleUrls: ['./index.component.scss'] +}) +export class IndexComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.html b/angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.html new file mode 100644 index 0000000..5543fb8 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.html @@ -0,0 +1,25 @@ +

Пригласи друзей!

+
+

+ Пригласи друзей зарегистрироваться в приложении по твоему уникальному коду и + получи бонусы, когда они совершат первую покупку. +

+ +
+ + + + diff --git a/angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.scss b/angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.scss new file mode 100644 index 0000000..0ff76c3 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.scss @@ -0,0 +1,33 @@ +:host { + padding: 16px; + h2 { + font-style: normal; + font-weight: 700; + font-size: 17px; + line-height: 22px; + letter-spacing: -0.408px; + } + + & .container { + width: 100%; + display: flex; + flex-direction: row; + gap: 16px; + + p { + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + color: #ffffff; + } + + .share { + width: 72px; + height: 48px; + background: #28af49; + padding: 8px 26px; + border: none; + } + } +} diff --git a/angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.spec.ts b/angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.spec.ts new file mode 100644 index 0000000..2c9c394 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { InviteFriendsComponent } from './invite-friends.component'; + +describe('InviteFriendsComponent', () => { + let component: InviteFriendsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ InviteFriendsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(InviteFriendsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.ts b/angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.ts new file mode 100644 index 0000000..b05b189 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/invite-friends/invite-friends.component.ts @@ -0,0 +1,54 @@ +import { Component, OnInit } from '@angular/core'; +import { MessageService } from 'primeng/api'; +import { lastValueFrom } from 'rxjs'; +import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; +import { environment } from 'src/environments/environment'; + +@Component({ + selector: 'app-invite-friends', + templateUrl: './invite-friends.component.html', + styleUrls: ['./invite-friends.component.scss'] +}) +export class InviteFriendsComponent implements OnInit { + public refUrl: string = `${environment.defaultUrl}/?refUserId=` + public loading: boolean = true; + private shareData: ShareData = { + title: '' + } + + constructor( + private jsonrpc: JsonrpcService, + private messageService: MessageService + ) { } + + async ngOnInit() { + const accountData = (await lastValueFrom( + this.jsonrpc + .rpc( + { + method: 'getTokenData', + params: [], + }, + RpcService.authService, + true + ) + )).data + + this.refUrl += accountData.user_id + this.loading = false + } + + share() { + if (navigator.share) { + navigator.share({ + title: document.title, + text: "Coffee Like", + url: this.refUrl + }) + .then(() => console.log('Successful share')) + .catch((error) => { + console.log('Error sharing:', error) + }); + } + } +} diff --git a/angular/src/app/presentation-options/first-option/components/last-order/last-order.component.html b/angular/src/app/presentation-options/first-option/components/last-order/last-order.component.html new file mode 100644 index 0000000..a5e4b7e --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/last-order/last-order.component.html @@ -0,0 +1,31 @@ +

Ваш предыдущий заказ

+
+

Дата: + {{(lastOrder?.transactionCreateDate | date:'dd.MM.yyyyг.') || 'Данные не найдены'}} + + + +

+

На сумму: + {{lastOrder?.orderSum ? lastOrder?.orderSum + ' ₽' : 'Данные не найдены'}} + + + +

+
+ + + +

+ Списание бонусов возможно на любые категории. Бонусами можно оплатить 100% + суммы покупки. Бонусы начисляются только на напитки с учётом добавок. + Неиспользованные бонусы сгорают в течение 90 дней. +

+ + + + \ No newline at end of file diff --git a/angular/src/app/presentation-options/first-option/components/last-order/last-order.component.scss b/angular/src/app/presentation-options/first-option/components/last-order/last-order.component.scss new file mode 100644 index 0000000..4c5a2c0 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/last-order/last-order.component.scss @@ -0,0 +1,51 @@ +:host { + padding: 24px 16px 56px; + + & > h2 { + font-style: normal; + font-weight: 700; + font-size: 15px; + line-height: 20px; + letter-spacing: -0.24px; + } + + & > .info-order { + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + span { + color: #828282; + } + .flex { + display: flex; + align-items: center; + flex-direction: row; + gap: 8px; + } + } + + .evaluate-order { + margin: 24px 0; + width: 100%; + padding: 12px; + text-align: center; + border: 2px solid #28af49; + border-radius: 6px; + font-style: normal; + font-weight: 700; + font-size: 17px; + line-height: 22px; + letter-spacing: -0.408px; + background-color: transparent; + color: #28af49; + } + + .info { + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + color: rgba(255, 255, 255, 0.5); + } +} diff --git a/angular/src/app/presentation-options/first-option/components/last-order/last-order.component.spec.ts b/angular/src/app/presentation-options/first-option/components/last-order/last-order.component.spec.ts new file mode 100644 index 0000000..101c8ed --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/last-order/last-order.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LastOrderComponent } from './last-order.component'; + +describe('LastOrderComponent', () => { + let component: LastOrderComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ LastOrderComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LastOrderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/first-option/components/last-order/last-order.component.ts b/angular/src/app/presentation-options/first-option/components/last-order/last-order.component.ts new file mode 100644 index 0000000..0117951 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/last-order/last-order.component.ts @@ -0,0 +1,18 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { Purchase } from 'src/app/interface/data'; + +@Component({ + selector: 'app-last-order[lastOrder]', + templateUrl: './last-order.component.html', + styleUrls: ['./last-order.component.scss'] +}) +export class LastOrderComponent implements OnInit { + @Input() lastOrder!: Purchase; + @Input() loading!: boolean; + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/angular/src/app/presentation-options/first-option/components/navbar/navbar.component.html b/angular/src/app/presentation-options/first-option/components/navbar/navbar.component.html new file mode 100644 index 0000000..e3df091 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/navbar/navbar.component.html @@ -0,0 +1,5 @@ +
+ +

{{title}}

+
+
diff --git a/angular/src/app/presentation-options/first-option/components/navbar/navbar.component.scss b/angular/src/app/presentation-options/first-option/components/navbar/navbar.component.scss new file mode 100644 index 0000000..dcd7a52 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/navbar/navbar.component.scss @@ -0,0 +1,33 @@ +:host { + width: 100%; +} + +.container { + box-sizing: border-box; + padding: 12px 16px; + width: 100%; + background: #231f20; + box-shadow: 0px 8px 16px rgba(17, 17, 17, 0.5); + color: #fff; + display: flex; + align-items: center; + justify-content: space-between; + .back-arrow { + font-size: 16px; + display: flex; + align-items: center; + justify-content: center; + } + .plug { + height: 24px; + width: 24px; + visibility: hidden; + } + .title { + font-family: "Gotham Pro", sans-serif; + font-weight: 700; + font-size: 17px; + line-height: 22px; + margin: 0; + } +} diff --git a/angular/src/app/presentation-options/first-option/components/navbar/navbar.component.spec.ts b/angular/src/app/presentation-options/first-option/components/navbar/navbar.component.spec.ts new file mode 100644 index 0000000..505cc2f --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/navbar/navbar.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NavbarComponent } from './navbar.component'; + +describe('NavbarComponent', () => { + let component: NavbarComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ NavbarComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(NavbarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/first-option/components/navbar/navbar.component.ts b/angular/src/app/presentation-options/first-option/components/navbar/navbar.component.ts new file mode 100644 index 0000000..f0300d0 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/navbar/navbar.component.ts @@ -0,0 +1,21 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +@Component({ + selector: 'app-navbar[title]', + templateUrl: './navbar.component.html', + styleUrls: ['./navbar.component.scss'] +}) +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/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.html b/angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.html new file mode 100644 index 0000000..98fc383 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.html @@ -0,0 +1,3 @@ + + + diff --git a/angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.scss b/angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.scss new file mode 100644 index 0000000..6cd7512 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.scss @@ -0,0 +1,15 @@ +:host { + display: flex; + flex-direction: row; + gap: 16px; + justify-content: space-between; + a { + width: 48px; + height: 48px; + border-radius: 100%; + background: #333333; + display: flex; + align-items: center; + justify-content: center; + } +} \ No newline at end of file diff --git a/angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.spec.ts b/angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.spec.ts new file mode 100644 index 0000000..b8cd680 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SocialMediaButtonsComponent } from './social-media-buttons.component'; + +describe('SocialMediaButtonsComponent', () => { + let component: SocialMediaButtonsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ SocialMediaButtonsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SocialMediaButtonsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.ts b/angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.ts new file mode 100644 index 0000000..2c659b0 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/components/social-media-buttons/social-media-buttons.component.ts @@ -0,0 +1,38 @@ +import { Component, OnInit } from '@angular/core'; + +export interface ISocialMediaLink { + url: string; + imgUrl: string; + label: string; +} + +@Component({ + selector: 'app-social-media-buttons', + templateUrl: './social-media-buttons.component.html', + styleUrls: ['./social-media-buttons.component.scss'] +}) +export class SocialMediaButtonsComponent implements OnInit { + public links: ISocialMediaLink[] = [ + { + label: 'Инстаграм', + url: 'https://www.instagram.com/', + imgUrl: '/assets/social-media-icons/instagram.svg' + }, + { + label: 'ВК', + url: 'https://vk.com/coffeelike_com', + imgUrl: '/assets/social-media-icons/vk.svg' + }, + { + label: 'Youtube', + url: 'https://www.youtube.com/c/coffeelikeru', + imgUrl: '/assets/social-media-icons/youtube.svg' + } + ] + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/angular/src/app/presentation-options/first-option/first-option-routing.module.ts b/angular/src/app/presentation-options/first-option/first-option-routing.module.ts new file mode 100644 index 0000000..6097315 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/first-option-routing.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { GuestCardComponent } from './pages/guest-card/guest-card.component'; +import { AuthGuard } from 'src/app/guards/auth-guard.guard'; +import { LoginComponent } from './pages/login/login.component'; + +const routes: Routes = [ + { + path: '', + component: GuestCardComponent, + canActivate: [AuthGuard], + }, + { + path: 'login', + component: LoginComponent + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class FirstOptionRoutingModule {} diff --git a/angular/src/app/presentation-options/first-option/first-option.module.ts b/angular/src/app/presentation-options/first-option/first-option.module.ts new file mode 100644 index 0000000..290573b --- /dev/null +++ b/angular/src/app/presentation-options/first-option/first-option.module.ts @@ -0,0 +1,51 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FirstOptionRoutingModule } from './first-option-routing.module'; +import { IndexComponent } from './components/index/index.component'; +import { FooterComponent } from './components/footer/footer.component'; +import { SocialMediaButtonsComponent } from './components/social-media-buttons/social-media-buttons.component'; +import { NavbarComponent } from './components/navbar/navbar.component'; +import { MatIconModule } from '@angular/material/icon'; +import { GuestCardComponent } from './pages/guest-card/guest-card.component'; +import { QrCodeModule } from 'ng-qrcode'; +import { AccordionComponent } from './components/accordion/accordion.component'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { LastOrderComponent } from './components/last-order/last-order.component'; +import { InviteFriendsComponent } from './components/invite-friends/invite-friends.component'; +import { LoginComponent } from './pages/login/login.component'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { NgxMatIntlTelInputComponent } from 'ngx-mat-intl-tel-input'; +import { MatInputModule } from '@angular/material/input'; +import { DirectivesModule } from 'src/app/directives/directives.module'; + + + +@NgModule({ + declarations: [ + IndexComponent, + FooterComponent, + SocialMediaButtonsComponent, + NavbarComponent, + GuestCardComponent, + AccordionComponent, + LastOrderComponent, + InviteFriendsComponent, + LoginComponent + ], + imports: [ + CommonModule, + FirstOptionRoutingModule, + MatIconModule, + QrCodeModule, + MatProgressSpinnerModule, + FormsModule, + MatFormFieldModule, + NgxMatIntlTelInputComponent, + ReactiveFormsModule, + MatInputModule, + DirectivesModule + ], + bootstrap: [IndexComponent], +}) +export class FirstOptionModule { } diff --git a/angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.html b/angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.html new file mode 100644 index 0000000..4681ca6 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.html @@ -0,0 +1,198 @@ + + +
+
+ + + + + + +
+
+ Текущий баланс бонусов: + + {{ getBalanceAmount(customerInfo?.walletBalances) }} + бонусов +
+ + +

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

+

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

+

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

+
+ +

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

+

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

+

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

+

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

+

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

+
+ +

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

+ +
    + Возврат покупки, за которую бонусы были начислены: +
  • + В случае, если бонусов на счету достаточно для списания, бонусы + списываются в полном ранее начисленном за возвращаемый товар объеме. +
  • +
  • + В случае, если бонусов на счету недостаточно, формируется минусовой + баланс. +
  • +
+ +
    + Возврат покупки, которая была оплачена бонусами: +
  • + В случае предъявления Участником кассового или товарного чека, сумма + бонусов, списанная для оплаты возвращаемого товара, зачисляется на счет + участника. +
  • +
  • + В случае возврата товара с применением оплаты бонусами, клиенту + возвращается денежная сумма в размере, внесенном Участником в оплату + товара при покупке, за вычетом суммы, оплаченной бонусами. +
  • +
+
+ +
+ Сумма ваших покупок за период с + {{ purchaseData.currentPeriod[0].format("DD.MM.yyyyг.") }} - + {{ purchaseData.currentAmount }} руб. + + + +
+ +

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

+ + + +
    + Уровень {{ index + 1 }} +
  • + Сумма покупок за предыдущий период {{ item.start }}-{{ item.end }} + руб. +
  • +
  • Начисляемый бонус {{ item.percent }}% от суммы покупки
  • +
+
+
+ +
    + Уровень {{ index + 1 }} +
  • Сумма покупок за предыдущий период — от {{ item.start }} руб.
  • +
  • Начисляемый бонус, в % от суммы покупки - {{ item.percent }}%
  • +
+
+
+
+ +
+ + +

+ До следующего уровня за период с + {{ purchaseData.currentPeriod[0].format("DD.MM.yyyyг") }} по + {{ purchaseData.currentPeriod[1].format("DD.MM.yyyyг") }} + осталось совершить покупки на {{ currentLvlPeriod.end - (purchaseData.currentAmount || 0) + 1 }} рублей +

+ +
+ +

+ У Вас последний уровень бонусной программы. Процент начисляемых бонусов: {{currentLvlPeriod.percent}}% +

+
+
+ + + + +

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

+
+
+ +
+ +
+
+ Скачай приложение +
+ Подробнее о правилах
+ Программы лояльности
+
+ + + + diff --git a/angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.scss b/angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.scss new file mode 100644 index 0000000..d94d12a --- /dev/null +++ b/angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.scss @@ -0,0 +1,200 @@ +:host { + .guest-card { + display: flex; + flex-direction: column; + align-items: center; + padding: 8px 0 0; + max-width: 600px; + margin: 0 auto; + color: red !important; + + &__qr { + padding: 10px; + width: fit-content; + background-image: linear-gradient( + #fff 33%, + transparent 0px, + transparent 67%, + #fff 0px + ), + linear-gradient( + 90deg, + #ffe 33%, + transparent 0px, + transparent 66%, + #fff 0px + ), + linear-gradient(#fff 33%, transparent 0px, transparent 67%, #fff 0), + linear-gradient(90deg, #fff 33%, transparent 0, transparent 66%, #fff 0); + background-size: 1px 100%, 100% 1px, 1px 100%, 100% 1px; + background-position: 0 0, 0 0, 100% 100%, 100% 100%; + background-repeat: no-repeat, no-repeat, no-repeat, no-repeat; + cursor: pointer; + } + + &__user-description { + margin: 18px 0 0; + padding: 14px 24px; + width: 100%; + text-align: left; + border-top: 1px solid #fff; + border-bottom: 1px solid #fff; + // font-family: "Goldman"; + font-style: normal; + font-weight: 400; + font-size: 16px; + line-height: 19px; + letter-spacing: -0.5px; + + span { + color: #f2994a; + } + } + + &__purchases-description { + margin: 0; + padding: 14px 24px; + width: 100%; + text-align: left; + border-bottom: 1px solid #fff; + // font-family: "Goldman"; + font-style: normal; + font-weight: 400; + font-size: 16px; + line-height: 19px; + letter-spacing: -0.5px; + grid-template-columns: calc(100% - 24px) 24px; + + span { + color: #219653; + } + } + + &__level-info { + padding: 36px; + display: flex; + flex-direction: column; + align-items: center; + + h2 { + font-style: normal; + font-weight: 700; + font-size: 17px; + line-height: 22px; + text-align: center; + letter-spacing: -0.408px; + } + + input[type="range"] { + -webkit-appearance: none; + width: 100%; + height: 6px; + border-radius: 5px; + display: block; + position: relative; + background: #231f20; + box-shadow: 0px 0px 3px #f2c94c59; + background-image: linear-gradient(#f2c94c, #f2c94c); + background-size: 70% 100%; + background-repeat: no-repeat; + + &::before, + &::after { + content: " "; + display: block; + width: 16px; + height: 16px; + border-radius: 100%; + position: absolute; + top: -5px; + } + + &::before { + background-color: #f2c94c; + left: 0px; + } + + &::after { + background-color: #f2c94c; + right: 0px; + } + + &::-webkit-slider-thumb { + -webkit-appearance: none; + background: #f2c94c; + width: 16px; + height: 16px; + border-radius: 100%; + display: none; + } + + &::-ms-thumb { + -webkit-appearance: none; + height: 20px; + width: 20px; + border-radius: 50%; + background: #f2c94c; + cursor: ew-resize; + box-shadow: 0 0 2px 0 #555; + transition: background 0.3s ease-in-out; + } + + &::-moz-range-thumb { + -webkit-appearance: none; + height: 20px; + width: 20px; + border-radius: 50%; + background: #f2c94c; + cursor: ew-resize; + box-shadow: 0 0 2px 0 #555; + transition: background 0.3s ease-in-out; + } + } + + & > .show-more { + margin-top: 42px; + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + text-align: center; + a { + text-decoration: none; + color: #28af49; + } + } + } + + &__download-app { + width: 100%; + position: relative; + margin-top: 32px; + display: flex; + justify-content: flex-end; + img { + width: 100%; + max-width: calc(100% - 16px); + } + } + + &__loyalty-program { + text-align: center; + color: rgba(255, 255, 255, 0.5); + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + text-decoration: none; + margin: 17px 0 22px; + } + } +} + +app-accordion { + ul { + li { + list-style-type: disc; + margin-left: 16px; + } + } +} diff --git a/angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.spec.ts b/angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.spec.ts new file mode 100644 index 0000000..f9a4d88 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { GuestCardComponent } from './guest-card.component'; + +describe('GuestCardComponent', () => { + let component: GuestCardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ GuestCardComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(GuestCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.ts b/angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.ts new file mode 100644 index 0000000..dc9120b --- /dev/null +++ b/angular/src/app/presentation-options/first-option/pages/guest-card/guest-card.component.ts @@ -0,0 +1,182 @@ +import { Component, OnInit } from '@angular/core'; +import { MatBottomSheet } from '@angular/material/bottom-sheet'; +import { Router } from '@angular/router'; +import { Observable } from 'rxjs'; +import { ExitComponent } from 'src/app/components/exit/exit.component'; +import { CookiesService } from 'src/app/services/cookies.service'; +import { WpJsonService } from 'src/app/services/wp-json.service'; +import { environment } from 'src/environments/environment'; +import moment from 'moment'; +import { Purchase, lvlPeriod } from 'src/app/interface/data'; +import { lvlPeriods } from 'src/app/app.constants'; + +interface Moment extends moment.Moment {} + +export interface IPurchaseData { + currentPeriod: Moment[]; + lastPeriod: Moment[]; + lastPurchases: Purchase[]; + currentPurchases: Purchase[]; + lastAmount?: number; + currentAmount?: number; + $loading: boolean; +} + +@Component({ + selector: 'app-guest-card', + templateUrl: './guest-card.component.html', + styleUrls: ['./guest-card.component.scss'], +}) +export class GuestCardComponent implements OnInit { + public qrCodeSize: number = 85; + private isQrCodeClicked: boolean = false; + public customerInfo!: any; + public purchases!: Purchase[]; + public lastPurchase!: Purchase; + public purchaseData: IPurchaseData = { + currentPeriod: [], + lastPeriod: [], + lastPurchases: [], + currentPurchases: [], + $loading: true, + get currentAmount():number { + const amount = this.currentPurchases.reduce((accumulator, currentValue) => { + if (currentValue.transactionType !== 'PayFromWallet') return accumulator; + return accumulator + currentValue.orderSum!; + }, 0); + + return amount * 1 + }, + get lastAmount():number { + const amount = this.lastPurchases.reduce((accumulator, currentValue) => { + if (currentValue.transactionType !== 'PayFromWallet') return accumulator; + return accumulator + currentValue.orderSum!; + }, 0); + return amount * 1 + } + }; + + public discountLevel: number = 4.2; + public lvlPeriods: lvlPeriod[] = lvlPeriods; + public currentLvlPeriod!: lvlPeriod; + + constructor( + private _bottomSheet: MatBottomSheet, + private cookiesService: CookiesService, + private router: Router, + private wpJsonService: WpJsonService + ) {} + + ngOnInit(): void { + const token = this.cookiesService.getItem('token'); + this.getCurrentQuarterOfYear(); + this.wpJsonService + .getCustomerInfo( + environment.systemId, + token || '', + environment.icardProxy + ) + .subscribe({ + next: (value) => { + this.customerInfo = value.customer_info; + this.getPurchases().subscribe((value) => { + this.purchases = value[this.customerInfo?.id].filter( + (purchase: Purchase) => + [ + 'PayFromWallet', + 'CancelPayFromWallet', + 'RefillWallet', + 'RefillWalletFromOrder' + ].includes(purchase.transactionType || '') + ); + this.lastPurchase = this.purchases.filter( + (purchase: Purchase) => + [ + 'PayFromWallet', + 'RefillWalletFromOrder' + ].includes(purchase.transactionType || '') + )[0] + + this.purchaseData.lastPurchases = this.purchases.filter((value: Purchase) => { + return moment(value.transactionCreateDate).isBetween(this.purchaseData.lastPeriod[0], this.purchaseData.lastPeriod[1]) + }) + this.purchaseData.currentPurchases = this.purchases.filter((value: Purchase) => { + return moment(value.transactionCreateDate).isBetween(this.purchaseData.currentPeriod[0], this.purchaseData.currentPeriod[1]) + }) + const currentAmount = this.purchaseData.currentAmount || 0 + this.currentLvlPeriod = this.lvlPeriods.find((item) => item.start <= currentAmount && currentAmount <= (item.end || Infinity))! + this.purchaseData.$loading = false; + }); + }, + }); + } + + qrCodeClick() { + this.isQrCodeClicked = !this.isQrCodeClicked; + this.qrCodeSize = this.isQrCodeClicked ? 180 : 85; + } + + deleteToken(): void { + this.cookiesService.logout(); + } + + 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().subtract(1, 'months').startOf('month'), + end: Date | Moment = moment() + ): Observable { + const token = this.cookiesService.getItem('token'); + const delta = moment(end).diff(moment(start), 'days'); + return this.wpJsonService.getTransactions( + environment.systemId, + token ?? '', + environment.icardProxy + ); + } + + getCurrentQuarterOfYear() { + const quarters = [ + [ + moment().subtract(1, 'years').endOf('year').subtract(3, 'months'), + moment().subtract(1, 'years').endOf('year'), + ], + [moment().startOf('year'), moment().startOf('year').add(3, 'months')], + [ + moment().startOf('year').add(3, 'months'), + moment().startOf('year').add(6, 'months'), + ], + [ + moment().startOf('year').add(6, 'months'), + moment().startOf('year').add(9, 'months'), + ], + [ + moment().startOf('year').add(9, 'months'), + moment().startOf('year').add(12, 'months'), + ], + ]; + + for (let i = 0; i < 4; i++) { + if (moment().isBetween(quarters[i][0], quarters[i][1])) { + this.purchaseData.lastPeriod = quarters[i - 1]; + this.purchaseData.currentPeriod = quarters[i]; + } + } + } + + getBalanceAmount(loyaltyPrograms: any[]) { + return loyaltyPrograms.reduce((accumulator, currentValue) => { + return accumulator + currentValue.balance; + }, 0); + } +} diff --git a/angular/src/app/presentation-options/first-option/pages/login/login.component.html b/angular/src/app/presentation-options/first-option/pages/login/login.component.html new file mode 100644 index 0000000..2069964 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/pages/login/login.component.html @@ -0,0 +1,107 @@ + +

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

+

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

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

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

+ +
+ + +

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

+
+
+ + + + +
+ +
+

+ Не пришло SMS?
+ + Отправим повторно через {{timeLeft}}с + + + Отправить повторно + +

+
diff --git a/angular/src/app/presentation-options/first-option/pages/login/login.component.scss b/angular/src/app/presentation-options/first-option/pages/login/login.component.scss new file mode 100644 index 0000000..c0c7d67 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/pages/login/login.component.scss @@ -0,0 +1,215 @@ +:host { + padding-top: 48px; + display: flex; + flex-direction: column; + align-items: center; + max-width: 600px; + margin: 0 auto 52px; + color: red !important; + + h1 { + margin-top: 20px; + width: 302px; + font-style: normal; + font-weight: 700; + font-size: 17px; + line-height: 22px; + text-align: center; + letter-spacing: -0.408px; + } + + h2 { + margin-top: 44px; + font-style: normal; + font-weight: 700; + font-size: 20px; + line-height: 24px; + text-align: center; + letter-spacing: 0.38px; + } + + .description { + width: 180px; + font-style: normal; + font-weight: 400; + font-size: 15px; + line-height: 20px; + text-align: center; + letter-spacing: -0.24px; + margin-top: 16px; + } + + form { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 35px; + + .offer { + margin-top: 10px; + padding: 0 16px; + font-family: "Gowun Dodum"; + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 17px; + text-align: center; + span { + color: #13a538; + } + } + + .input-container { + position: relative; + width: 100%; + + label { + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + color: #6a737c; + text-align: left; + position: absolute; + top: 10px; + left: 16px; + } + + 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; + } + } + + button { + height: 46px; + width: calc(100% - 32px); + padding: 10px 24px; + background: #28af49; + border-radius: 6px; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + border: none; + letter-spacing: -0.408px; + color: #ffffff; + font-style: normal; + 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; + // } + .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; + } + } + } + + .resend-code { + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + text-align: center; + margin-top: 23px; + .resend { + cursor: pointer; + } + } +} + +mat-form-field { + width: 100%; +} diff --git a/angular/src/app/presentation-options/first-option/pages/login/login.component.spec.ts b/angular/src/app/presentation-options/first-option/pages/login/login.component.spec.ts new file mode 100644 index 0000000..10eca24 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/pages/login/login.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginComponent } from './login.component'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ LoginComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/first-option/pages/login/login.component.ts b/angular/src/app/presentation-options/first-option/pages/login/login.component.ts new file mode 100644 index 0000000..0a60904 --- /dev/null +++ b/angular/src/app/presentation-options/first-option/pages/login/login.component.ts @@ -0,0 +1,167 @@ +import { + AfterViewInit, + Component, + EventEmitter, + HostListener, + OnInit +} from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { MatSnackBar } from '@angular/material/snack-bar'; +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, AfterViewInit { + public isShowNumber: boolean = true; + public phoneForm = new FormGroup({ + name: new FormControl('', []), + 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']; + timeLeft: number = 0; + + constructor( + private cookiesService: CookiesService, + private router: Router, + private jsonrpc: JsonrpcService, + private messageService: MessageService, + private _snackBar: MatSnackBar + ) {} + + 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; + this.isShowNumber = false; + if (this.timeLeft) { + this.messageService.add({ + severity: 'custom', + summary: `Отправить повторно можно через ${this.timeLeft}с`, + }); + return; + } + this.jsonrpc.rpc({ + method: 'sendVerifyByPhone', + params: [data.phone] + }, RpcService.authService, false).subscribe({ + next: (result) => { + if (result.code !== 0) { + this._snackBar.open('Произошла ошибка! Попробуйте позже', '', { + duration: 3000 + }) + } + if (result.code === 0) { + this.timeLeft = 60; + const interval = setInterval(() => { + if(this.timeLeft > 0) { + this.timeLeft--; + } else { + clearInterval(interval); + } + },1000) + } + this.isShowNumber = false; + }, + error: (error) => { + console.error('Error: ', error); + + } + } + ); + setTimeout(() => { + this.inputFocusEmitter.emit(`#${this.inputIds[0]}`); + }, 0) + } + + submitCode() { + const data = this.codeForm.value; + this.jsonrpc.rpc({ + method: 'getTokenByPhone', + params: [this.phoneForm.value.phone, Object.values(data).join('')] + }, RpcService.authService, false).subscribe({ + next: (result) => { + if (result.code === 0) { + this.cookiesService.setCookie('token', result?.data?.token); + this.router.navigate(['/'], { + queryParams: { + token: result?.data?.token + }, + }); + // this.phoneConfirmed.emit(null); + } else if (result.code === 230) { + this._snackBar.open('Неверный код!', '', { + duration: 3000 + }) + // 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/presentation-options/second-option/components/test/test.component.html b/angular/src/app/presentation-options/second-option/components/test/test.component.html new file mode 100644 index 0000000..941f267 --- /dev/null +++ b/angular/src/app/presentation-options/second-option/components/test/test.component.html @@ -0,0 +1 @@ +

test works!

diff --git a/angular/src/app/presentation-options/second-option/components/test/test.component.scss b/angular/src/app/presentation-options/second-option/components/test/test.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/angular/src/app/presentation-options/second-option/components/test/test.component.spec.ts b/angular/src/app/presentation-options/second-option/components/test/test.component.spec.ts new file mode 100644 index 0000000..f55af9d --- /dev/null +++ b/angular/src/app/presentation-options/second-option/components/test/test.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TestComponent } from './test.component'; + +describe('TestComponent', () => { + let component: TestComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ TestComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(TestComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/presentation-options/second-option/components/test/test.component.ts b/angular/src/app/presentation-options/second-option/components/test/test.component.ts new file mode 100644 index 0000000..db9004b --- /dev/null +++ b/angular/src/app/presentation-options/second-option/components/test/test.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-test', + templateUrl: './test.component.html', + styleUrls: ['./test.component.scss'] +}) +export class TestComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/angular/src/app/presentation-options/second-option/second-option-routing.module.ts b/angular/src/app/presentation-options/second-option/second-option-routing.module.ts new file mode 100644 index 0000000..2abda6a --- /dev/null +++ b/angular/src/app/presentation-options/second-option/second-option-routing.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { TestComponent } from './components/test/test.component'; + +const routes: Routes = [ + { + path: '', + component: TestComponent, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class SecondOptionRoutingModule {} diff --git a/angular/src/app/presentation-options/second-option/second-option.module.ts b/angular/src/app/presentation-options/second-option/second-option.module.ts new file mode 100644 index 0000000..5faee36 --- /dev/null +++ b/angular/src/app/presentation-options/second-option/second-option.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { TestComponent } from './components/test/test.component'; +import { SecondOptionRoutingModule } from './second-option-routing.module'; + + + +@NgModule({ + declarations: [ + TestComponent + ], + imports: [ + CommonModule, + SecondOptionRoutingModule + ] +}) +export class SecondOptionModule { } diff --git a/angular/src/app/services/cookies.service.ts b/angular/src/app/services/cookies.service.ts index 3cb3675..daeefc5 100644 --- a/angular/src/app/services/cookies.service.ts +++ b/angular/src/app/services/cookies.service.ts @@ -42,4 +42,10 @@ export class CookiesService { deleteCookie(key: string): void { this.setCookie(key, '', { 'max-age': -1 }); } + + logout() { + this.deleteCookie('token') + this.deleteCookie('phone-number') + } + } diff --git a/angular/src/app/services/loyalty-program.service.ts b/angular/src/app/services/loyalty-program.service.ts new file mode 100644 index 0000000..67f9aa8 --- /dev/null +++ b/angular/src/app/services/loyalty-program.service.ts @@ -0,0 +1,128 @@ +import { Injectable } from '@angular/core'; +import moment from 'moment'; +import { Moment, Purchase } from '../interface/data'; + +export interface IPurchaseData { + currentPeriod: Moment[]; + lastPeriod: Moment[]; + lastPurchases: Purchase[]; + currentPurchases: Purchase[]; + lastAmount?: number; + currentAmount?: number; + $loading: boolean; +} + +@Injectable({ + providedIn: 'root', +}) +export class LoyaltyProgramService { + public purchaseData: IPurchaseData = { + currentPeriod: [], + lastPeriod: [], + lastPurchases: [], + currentPurchases: [], + $loading: true, + get currentAmount(): number { + const amount = this.currentPurchases.reduce( + (accumulator, currentValue) => { + if (['CancelPayFromWallet', 'CancelRefillWalletFromOrder'].includes(currentValue.transactionType || '')) { + return accumulator - currentValue.orderSum!; + } else if (['PayFromWallet', 'RefillWalletFromOrder'].includes(currentValue.transactionType || '')) { + return accumulator + currentValue.orderSum!; + } + return accumulator + }, + 0 + ); + + return amount * 1; + }, + get lastAmount(): number { + const amount = this.lastPurchases.reduce((accumulator, currentValue) => { + if (['CancelPayFromWallet', 'CancelRefillWalletFromOrder'].includes(currentValue.transactionType || '')) { + return accumulator - currentValue.orderSum!; + } else if (['PayFromWallet', 'RefillWalletFromOrder'].includes(currentValue.transactionType || '')) { + return accumulator + currentValue.orderSum!; + } + return accumulator + }, 0); + return amount * 1; + }, + }; + + constructor() { + this.getCurrentQuarterOfYear(); + } + + getCurrentQuarterOfYear() { + const quarters = [ + [ + moment().subtract(1, 'years').endOf('year').subtract(3, 'months'), + moment().startOf('year').add(10, 'days'), + ], + [moment().startOf('year').add(10, 'days'), moment().startOf('year').add(3, 'months')], + [ + moment().startOf('year').add(3, 'months'), + moment().startOf('year').add(6, 'months'), + ], + [ + moment().startOf('year').add(6, 'months'), + moment().startOf('year').add(9, 'months'), + ], + [ + moment().startOf('year').add(9, 'months'), + moment().startOf('year').add(12, 'months'), + ], + ]; + + for (let i = 0; i < 4; i++) { + if (moment().isBetween(quarters[i][0], quarters[i][1])) { + this.purchaseData.lastPeriod = quarters[i - 1]; + this.purchaseData.currentPeriod = quarters[i]; + } + } + } + + getBalanceAmount(loyaltyPrograms: any[]) { + return loyaltyPrograms.reduce((accumulator, currentValue) => { + return accumulator + currentValue.balance; + }, 0); + } + + setLastPurchases(purchases: Purchase[]) { + this.purchaseData.lastPurchases = purchases.filter((value: Purchase) => { + return moment(value.transactionCreateDate).isBetween( + this.purchaseData.lastPeriod[0], + this.purchaseData.lastPeriod[1] + ); + }); + } + + setCurrentPurchases(purchases: Purchase[]) { + this.purchaseData.currentPurchases = purchases.filter((value: Purchase) => { + return moment(value.transactionCreateDate).isBetween( + this.purchaseData.currentPeriod[0], + this.purchaseData.currentPeriod[1] + ); + }); + } + + filterPurchases(purchases: Purchase[]) { + return purchases.filter((purchase: Purchase) => + [ + 'PayFromWallet', + 'RefillWalletFromOrder', + 'CancelPayFromWallet', + 'CancelRefillWalletFromOrder', + ].includes(purchase.transactionType || '') + ); + } + + getLastPurchase(purchases: Purchase[]) { + return purchases.filter((purchase: Purchase) => + ['PayFromWallet', 'RefillWalletFromOrder'].includes( + purchase.transactionType || '' + ) + )[0]; + } +}