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 @@
+
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 @@
+
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];
+ }
+}