This commit is contained in:
gofnnp 2025-12-19 13:02:46 +04:00
parent a8240ed0db
commit c51b47955d
227 changed files with 38372 additions and 38372 deletions

84
.gitignore vendored
View File

@ -1,42 +1,42 @@
# See http://help.github.com/ignore-files/ for more about ignoring files. # See http://help.github.com/ignore-files/ for more about ignoring files.
# Compiled output # Compiled output
angular/dist angular/dist
angular/tmp angular/tmp
angular/out-tsc angular/out-tsc
angular/bazel-out angular/bazel-out
# Node # Node
angular/node_modules angular/node_modules
angular/npm-debug.log angular/npm-debug.log
angular/yarn-error.log angular/yarn-error.log
# IDEs and editors # IDEs and editors
angular/.idea/ angular/.idea/
angular/.project angular/.project
angular/.classpath angular/.classpath
angular/.c9/ angular/.c9/
angular/*.launch angular/*.launch
angular/.settings/ angular/.settings/
angular/*.sublime-workspace angular/*.sublime-workspace
# Visual Studio Code # Visual Studio Code
angular/.vscode/* angular/.vscode/*
angular/!.vscode/settings.json angular/!.vscode/settings.json
angular/!.vscode/tasks.json angular/!.vscode/tasks.json
angular/!.vscode/launch.json angular/!.vscode/launch.json
angular/!.vscode/extensions.json angular/!.vscode/extensions.json
angular/.history/* angular/.history/*
# Miscellaneous # Miscellaneous
angular/.angular/cache angular/.angular/cache
angular/.sass-cache/ angular/.sass-cache/
angular/connect.lock angular/connect.lock
angular/coverage angular/coverage
angular/libpeerconnection.log angular/libpeerconnection.log
angular/testem.log angular/testem.log
angular/typings angular/typings
# System files # System files
angular/.DS_Store angular/.DS_Store
angular/Thumbs.db angular/Thumbs.db

60
Jenkinsfile vendored
View File

@ -1,30 +1,30 @@
env.HL_BUILD_MODE = "jenkins" env.HL_BUILD_MODE = "jenkins"
node('Palladium'){ node('Palladium'){
stage('get new version to repo') { stage('get new version to repo') {
checkout scm checkout scm
if (lastCommitIsBumpCommit()) { if (lastCommitIsBumpCommit()) {
currentBuild.result = 'ABORTED' currentBuild.result = 'ABORTED'
error('Последний коммит - результат сборки jenkins') error('Последний коммит - результат сборки jenkins')
} }
//sh "git submodule update --init --recursive" //sh "git submodule update --init --recursive"
//sh "git submodule update --remote --merge" //sh "git submodule update --remote --merge"
} }
stage("build and publish"){ stage("build and publish"){
dir('angular'){ dir('angular'){
sh label: '', script: 'npm i' sh label: '', script: 'npm i'
sh label: '', script: 'npm run build' sh label: '', script: 'npm run build'
} }
} }
} }
private boolean lastCommitIsBumpCommit() { private boolean lastCommitIsBumpCommit() {
lastCommit = sh([script: 'git log -1', returnStdout: true]) lastCommit = sh([script: 'git log -1', returnStdout: true])
if (lastCommit.contains("Author: jenkins")) { if (lastCommit.contains("Author: jenkins")) {
return true return true
} else { } else {
return false return false
} }
} }

View File

@ -1,16 +1,16 @@
# Editor configuration, see https://editorconfig.org # Editor configuration, see https://editorconfig.org
root = true root = true
[*] [*]
charset = utf-8 charset = utf-8
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.ts] [*.ts]
quote_type = single quote_type = single
[*.md] [*.md]
max_line_length = off max_line_length = off
trim_trailing_whitespace = false trim_trailing_whitespace = false

View File

@ -1,27 +1,27 @@
# TastyCoffee # TastyCoffee
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.0.6. This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.0.6.
## Development server ## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
## Code scaffolding ## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build ## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
## Running unit tests ## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests ## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
## Further help ## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

View File

@ -1,131 +1,131 @@
{ {
"$schema": "./node_modules/@angular/cli/lib/config/schema.json", "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1, "version": 1,
"newProjectRoot": "projects", "newProjectRoot": "projects",
"projects": { "projects": {
"tasty-coffee": { "tasty-coffee": {
"projectType": "application", "projectType": "application",
"schematics": { "schematics": {
"@schematics/angular:component": { "@schematics/angular:component": {
"style": "scss" "style": "scss"
}, },
"i18n": { "i18n": {
"sourceLocale": "ru-RU" "sourceLocale": "ru-RU"
} }
}, },
"root": "", "root": "",
"sourceRoot": "src", "sourceRoot": "src",
"prefix": "app", "prefix": "app",
"architect": { "architect": {
"build": { "build": {
"builder": "@angular-devkit/build-angular:browser", "builder": "@angular-devkit/build-angular:browser",
"options": { "options": {
"outputPath": "/var/www/html/lk.crm4retail.ru/tasty-coffee", "outputPath": "/var/www/html/lk.crm4retail.ru/tasty-coffee",
"baseHref": "/", "baseHref": "/",
"index": "src/index.html", "index": "src/index.html",
"main": "src/main.ts", "main": "src/main.ts",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss", "inlineStyleLanguage": "scss",
"assets": [ "assets": [
"src/favicon.ico", "src/favicon.ico",
"src/assets", "src/assets",
"src/manifest.webmanifest", "src/manifest.webmanifest",
"src/firebase-messaging-sw.js", "src/firebase-messaging-sw.js",
"src/sw-master.js", "src/sw-master.js",
"src/sw-custom.js" "src/sw-custom.js"
], ],
"styles": [ "styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css", "node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css",
"node_modules/primeicons/primeicons.css", "node_modules/primeicons/primeicons.css",
"node_modules/primeng/resources/primeng.min.css", "node_modules/primeng/resources/primeng.min.css",
"node_modules/ngx-sharebuttons/themes/modern.scss", "node_modules/ngx-sharebuttons/themes/modern.scss",
"node_modules/ngx-sharebuttons/themes/material.scss", "node_modules/ngx-sharebuttons/themes/material.scss",
"src/styles.scss" "src/styles.scss"
], ],
"scripts": [], "scripts": [],
"serviceWorker": true, "serviceWorker": true,
"ngswConfigPath": "ngsw-config.json" "ngswConfigPath": "ngsw-config.json"
}, },
"configurations": { "configurations": {
"production": { "production": {
"budgets": [ "budgets": [
{ {
"type": "initial", "type": "initial",
"maximumWarning": "4mb", "maximumWarning": "4mb",
"maximumError": "5mb" "maximumError": "5mb"
}, },
{ {
"type": "anyComponentStyle", "type": "anyComponentStyle",
"maximumWarning": "2kb", "maximumWarning": "2kb",
"maximumError": "4kb" "maximumError": "4kb"
} }
], ],
"fileReplacements": [ "fileReplacements": [
{ {
"replace": "src/environments/environment.ts", "replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts" "with": "src/environments/environment.prod.ts"
} }
], ],
"outputHashing": "all" "outputHashing": "all"
}, },
"development": { "development": {
"buildOptimizer": false, "buildOptimizer": false,
"optimization": false, "optimization": false,
"vendorChunk": true, "vendorChunk": true,
"extractLicenses": false, "extractLicenses": false,
"sourceMap": true, "sourceMap": true,
"namedChunks": true "namedChunks": true
} }
}, },
"defaultConfiguration": "production" "defaultConfiguration": "production"
}, },
"serve": { "serve": {
"builder": "@angular-devkit/build-angular:dev-server", "builder": "@angular-devkit/build-angular:dev-server",
"configurations": { "configurations": {
"production": { "production": {
"browserTarget": "tasty-coffee:build:production" "browserTarget": "tasty-coffee:build:production"
}, },
"development": { "development": {
"browserTarget": "tasty-coffee:build:development", "browserTarget": "tasty-coffee:build:development",
"proxyConfig": "proxy.confi.json" "proxyConfig": "proxy.confi.json"
} }
}, },
"defaultConfiguration": "development" "defaultConfiguration": "development"
}, },
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular-devkit/build-angular:extract-i18n",
"options": { "options": {
"browserTarget": "tasty-coffee:build" "browserTarget": "tasty-coffee:build"
} }
}, },
"test": { "test": {
"builder": "@angular-devkit/build-angular:karma", "builder": "@angular-devkit/build-angular:karma",
"options": { "options": {
"main": "src/test.ts", "main": "src/test.ts",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json", "tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js", "karmaConfig": "karma.conf.js",
"inlineStyleLanguage": "scss", "inlineStyleLanguage": "scss",
"assets": [ "assets": [
"src/favicon.ico", "src/favicon.ico",
"src/firebase-messaging-sw.js", "src/firebase-messaging-sw.js",
"src/assets", "src/assets",
"src/manifest.webmanifest" "src/manifest.webmanifest"
], ],
"styles": [ "styles": [
"./node_modules/@angular/material/prebuilt-themes/purple-green.css", "./node_modules/@angular/material/prebuilt-themes/purple-green.css",
"src/styles.scss" "src/styles.scss"
], ],
"scripts": [] "scripts": []
} }
} }
} }
} }
}, },
"cli": { "cli": {
"analytics": false "analytics": false
} }
} }

View File

@ -1,44 +1,44 @@
// Karma configuration file, see link for more information // Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html // https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) { module.exports = function (config) {
config.set({ config.set({
basePath: '', basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'], frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [ plugins: [
require('karma-jasmine'), require('karma-jasmine'),
require('karma-chrome-launcher'), require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'), require('karma-jasmine-html-reporter'),
require('karma-coverage'), require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma') require('@angular-devkit/build-angular/plugins/karma')
], ],
client: { client: {
jasmine: { jasmine: {
// you can add configuration options for Jasmine here // you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false` // for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321` // or set a specific seed with `seed: 4321`
}, },
clearContext: false // leave Jasmine Spec Runner output visible in browser clearContext: false // leave Jasmine Spec Runner output visible in browser
}, },
jasmineHtmlReporter: { jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces suppressAll: true // removes the duplicated traces
}, },
coverageReporter: { coverageReporter: {
dir: require('path').join(__dirname, './coverage/card-project'), dir: require('path').join(__dirname, './coverage/card-project'),
subdir: '.', subdir: '.',
reporters: [ reporters: [
{ type: 'html' }, { type: 'html' },
{ type: 'text-summary' } { type: 'text-summary' }
] ]
}, },
reporters: ['progress', 'kjhtml'], reporters: ['progress', 'kjhtml'],
port: 9876, port: 9876,
colors: true, colors: true,
logLevel: config.LOG_INFO, logLevel: config.LOG_INFO,
autoWatch: true, autoWatch: true,
browsers: ['Chrome'], browsers: ['Chrome'],
singleRun: false, singleRun: false,
restartOnFileChange: true restartOnFileChange: true
}); });
}; };

View File

@ -1,30 +1,30 @@
{ {
"$schema": "./node_modules/@angular/service-worker/config/schema.json", "$schema": "./node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html", "index": "/index.html",
"assetGroups": [ "assetGroups": [
{ {
"name": "app", "name": "app",
"installMode": "prefetch", "installMode": "prefetch",
"resources": { "resources": {
"files": [ "files": [
"/favicon.ico", "/favicon.ico",
"/index.html", "/index.html",
"/manifest.webmanifest", "/manifest.webmanifest",
"/*.css", "/*.css",
"/*.js" "/*.js"
] ]
} }
}, },
{ {
"name": "assets", "name": "assets",
"installMode": "lazy", "installMode": "lazy",
"updateMode": "prefetch", "updateMode": "prefetch",
"resources": { "resources": {
"files": [ "files": [
"/assets/**", "/assets/**",
"/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)" "/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)"
] ]
} }
} }
] ]
} }

51032
angular/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,63 +1,63 @@
{ {
"name": "tasty-coffee", "name": "tasty-coffee",
"version": "0.0.2", "version": "0.0.2",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve --host 192.168.0.14", "start": "ng serve --host 192.168.0.58",
"build": "ng build --aot --configuration production --output-hashing none", "build": "ng build --aot --configuration production --output-hashing none",
"watch": "ng build --watch --configuration development", "watch": "ng build --watch --configuration development",
"test": "ng test" "test": "ng test"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^15.2.9", "@angular/animations": "^15.2.9",
"@angular/cdk": "^15.2.9", "@angular/cdk": "^15.2.9",
"@angular/common": "^15.2.9", "@angular/common": "^15.2.9",
"@angular/compiler": "^15.2.9", "@angular/compiler": "^15.2.9",
"@angular/core": "^15.2.9", "@angular/core": "^15.2.9",
"@angular/fire": "^7.5.0", "@angular/fire": "^7.5.0",
"@angular/forms": "^15.2.9", "@angular/forms": "^15.2.9",
"@angular/material": "^15.2.9", "@angular/material": "^15.2.9",
"@angular/material-moment-adapter": "^15.2.9", "@angular/material-moment-adapter": "^15.2.9",
"@angular/platform-browser": "^15.2.9", "@angular/platform-browser": "^15.2.9",
"@angular/platform-browser-dynamic": "^15.2.9", "@angular/platform-browser-dynamic": "^15.2.9",
"@angular/router": "^15.2.9", "@angular/router": "^15.2.9",
"@angular/service-worker": "^15.2.9", "@angular/service-worker": "^15.2.9",
"@fortawesome/angular-fontawesome": "^0.12.1", "@fortawesome/angular-fontawesome": "^0.12.1",
"@fortawesome/fontawesome-svg-core": "^6.2.0", "@fortawesome/fontawesome-svg-core": "^6.2.0",
"@fortawesome/free-brands-svg-icons": "^6.2.0", "@fortawesome/free-brands-svg-icons": "^6.2.0",
"@fortawesome/free-solid-svg-icons": "^6.2.0", "@fortawesome/free-solid-svg-icons": "^6.2.0",
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",
"@types/web": "^0.0.99", "@types/web": "^0.0.99",
"angular-moment-timezone": "^1.7.1", "angular-moment-timezone": "^1.7.1",
"angular2-text-mask": "^9.0.0", "angular2-text-mask": "^9.0.0",
"barcode-2-svg": "^0.3.3", "barcode-2-svg": "^0.3.3",
"firebase": "^9.9.3", "firebase": "^9.9.3",
"google-libphonenumber": "^3.2.30", "google-libphonenumber": "^3.2.30",
"jsbarcode": "^3.11.5", "jsbarcode": "^3.11.5",
"libphonenumber-js": "^1.10.28", "libphonenumber-js": "^1.10.28",
"ng-qrcode": "^8.0.1", "ng-qrcode": "^8.0.1",
"ngx-mat-intl-tel-input": "^5.0.0", "ngx-mat-intl-tel-input": "^5.0.0",
"ngx-sharebuttons": "^11.0.0", "ngx-sharebuttons": "^11.0.0",
"primeicons": "^5.0.0", "primeicons": "^5.0.0",
"primeng": "^15.4.1", "primeng": "^15.4.1",
"rxjs": "~7.5.0", "rxjs": "~7.5.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"zone.js": "~0.11.4" "zone.js": "~0.11.4"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^15.2.8", "@angular-devkit/build-angular": "^15.2.8",
"@angular/cli": "~15.2.8", "@angular/cli": "~15.2.8",
"@angular/compiler-cli": "^15.2.9", "@angular/compiler-cli": "^15.2.9",
"@types/google-libphonenumber": "^7.4.23", "@types/google-libphonenumber": "^7.4.23",
"@types/jasmine": "~4.0.0", "@types/jasmine": "~4.0.0",
"jasmine-core": "~4.1.0", "jasmine-core": "~4.1.0",
"karma": "~6.3.0", "karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0", "karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.0", "karma-coverage": "~2.2.0",
"karma-jasmine": "~5.0.0", "karma-jasmine": "~5.0.0",
"karma-jasmine-html-reporter": "~1.7.0", "karma-jasmine-html-reporter": "~1.7.0",
"typescript": "4.8" "typescript": "4.8"
} }
} }

View File

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

View File

@ -1,20 +1,20 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { MainComponent } from './pages/main/main.component'; import { MainComponent } from './pages/main/main.component';
import { AuthGuard } from './guards/auth-guard.guard'; import { AuthGuard } from './guards/auth-guard.guard';
const routes: Routes = [ const routes: Routes = [
{ {
path: '', path: '',
loadChildren: () => null as any, loadChildren: () => null as any,
// component: GuestCardComponent, // component: GuestCardComponent,
// canActivate: [AuthGuard] // canActivate: [AuthGuard]
}, },
// { path: '**', component: NotFoundComponent } // { path: '**', component: NotFoundComponent }
]; ];
@NgModule({ @NgModule({
imports: [RouterModule.forRoot(routes)], imports: [RouterModule.forRoot(routes)],
exports: [RouterModule] exports: [RouterModule]
}) })
export class AppRoutingModule { } export class AppRoutingModule { }

View File

@ -1,2 +1,2 @@
<router-outlet></router-outlet> <router-outlet></router-outlet>
<p-toast position="top-center"></p-toast> <p-toast position="top-center"></p-toast>

View File

@ -1,35 +1,35 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
describe('AppComponent', () => { describe('AppComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [ imports: [
RouterTestingModule RouterTestingModule
], ],
declarations: [ declarations: [
AppComponent AppComponent
], ],
}).compileComponents(); }).compileComponents();
}); });
it('should create the app', () => { it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance; const app = fixture.componentInstance;
expect(app).toBeTruthy(); expect(app).toBeTruthy();
}); });
it(`should have as title 'tasty-coffee'`, () => { it(`should have as title 'tasty-coffee'`, () => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance; const app = fixture.componentInstance;
expect(app.title).toEqual('tasty-coffee'); expect(app.title).toEqual('tasty-coffee');
}); });
it('should render title', () => { it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges(); fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement; const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('.content span')?.textContent).toContain('tasty-coffee app is running!'); expect(compiled.querySelector('.content span')?.textContent).toContain('tasty-coffee app is running!');
}); });
}); });

View File

@ -1,64 +1,64 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { RouteConfigLoadStart, Router } from '@angular/router'; import { RouteConfigLoadStart, Router } from '@angular/router';
import { AuthService } from './services/auth.service'; import { AuthService } from './services/auth.service';
import { CookiesService } from './services/cookies.service'; import { CookiesService } from './services/cookies.service';
import { JsonrpcService, RpcService } from './services/jsonrpc.service'; import { JsonrpcService, RpcService } from './services/jsonrpc.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'], styleUrls: ['./app.component.scss'],
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {
title = 'Tasty Coffee'; title = 'Tasty Coffee';
constructor( constructor(
private router: Router, private router: Router,
private cookiesService: CookiesService, private cookiesService: CookiesService,
private jsonRpcService: JsonrpcService, private jsonRpcService: JsonrpcService,
private authService: AuthService, private authService: AuthService,
) { ) {
this.router.events.subscribe((x) => { this.router.events.subscribe((x) => {
if (x instanceof RouteConfigLoadStart && x.route.path === '') { if (x instanceof RouteConfigLoadStart && x.route.path === '') {
x.route.loadChildren = () => { x.route.loadChildren = () => {
switch (this.cookiesService.getItem('presentation-option')) { switch (this.cookiesService.getItem('presentation-option')) {
case 'first': case 'first':
return import( return import(
'./presentation-options/first-option/first-option.module' './presentation-options/first-option/first-option.module'
).then((mod) => mod.FirstOptionModule); ).then((mod) => mod.FirstOptionModule);
default: default:
return import( return import(
'./presentation-options/default-option/default-option.module' './presentation-options/default-option/default-option.module'
).then((mod) => mod.DefaultOptionModule); ).then((mod) => mod.DefaultOptionModule);
} }
}; };
} }
}); });
} }
getAdditionalInfo() { getAdditionalInfo() {
return this.jsonRpcService.rpc( return this.jsonRpcService.rpc(
{ {
method: 'getAdditionalInfo', method: 'getAdditionalInfo',
params: [], params: [],
}, },
RpcService.authService, RpcService.authService,
true true
); );
} }
ngOnInit(): void { ngOnInit(): void {
if (this.authService.authorized) { if (this.authService.authorized) {
this.authService.getUserInfo(); this.authService.getUserInfo();
} }
this.getAdditionalInfo().subscribe({ this.getAdditionalInfo().subscribe({
next: (value) => { next: (value) => {
this.cookiesService.setCookie('presentation-option', value?.data?.interface_id || 'default') this.cookiesService.setCookie('presentation-option', value?.data?.interface_id || 'default')
}, },
error: (err) => { error: (err) => {
console.error(err); console.error(err);
} }
}) })
} }
} }

View File

@ -1,93 +1,93 @@
import { OrderStatus, Page, PageCode, lvlPeriod } from "./interface/data"; import { OrderStatus, Page, PageCode, lvlPeriod } from "./interface/data";
export const PageList: Page[] = [ export const PageList: Page[] = [
{ {
code: PageCode.Auth, code: PageCode.Auth,
name: 'Вход', name: 'Вход',
resName: 'auth', resName: 'auth',
onSideBar: false, onSideBar: false,
}, },
{ {
code: PageCode.Orders, code: PageCode.Orders,
name: 'Заказы', name: 'Заказы',
resName: 'orders', resName: 'orders',
onSideBar: true, onSideBar: true,
}, },
]; ];
export const PageListWithBonus: Page[] = [ export const PageListWithBonus: Page[] = [
{ {
code: PageCode.Auth, code: PageCode.Auth,
name: 'Вход', name: 'Вход',
resName: 'auth', resName: 'auth',
onSideBar: false, onSideBar: false,
}, },
{ {
code: PageCode.BonusProgram, code: PageCode.BonusProgram,
name: 'Ваша карта лояльности', name: 'Ваша карта лояльности',
description: '', description: '',
resName: 'bonus-program', resName: 'bonus-program',
onSideBar: true, onSideBar: true,
}, },
{ {
code: PageCode.Orders, code: PageCode.Orders,
name: 'Ваши чеки', name: 'Ваши чеки',
description: '', description: '',
resName: 'orders', resName: 'orders',
onSideBar: true, onSideBar: true,
}, },
{ {
code: PageCode.UserData, code: PageCode.UserData,
name: 'Заполнить анкету', name: 'Заполнить анкету',
description: '', description: '',
resName: 'user-data', resName: 'user-data',
onSideBar: true onSideBar: true
}, },
{ {
code: PageCode.RefSystem, code: PageCode.RefSystem,
name: 'Пригласить друга', name: 'Пригласить друга',
description: '', description: '',
resName: 'ref-system', resName: 'ref-system',
onSideBar: true, onSideBar: true,
} }
]; ];
export const orderStatuses: OrderStatus = { export const orderStatuses: OrderStatus = {
'Cancelled': 'Отменен', 'Cancelled': 'Отменен',
'InProcessing': 'В обработке', 'InProcessing': 'В обработке',
'Unconfirmed': 'Принят', 'Unconfirmed': 'Принят',
'WaitCooking': 'Принят', 'WaitCooking': 'Принят',
'ReadyForCooking': 'Принят', 'ReadyForCooking': 'Принят',
'CookingStarted': 'Готовится', 'CookingStarted': 'Готовится',
'CookingCompleted': 'Приготовлен', 'CookingCompleted': 'Приготовлен',
'Waiting': 'В пути', 'Waiting': 'В пути',
'OnWay': 'В пути', 'OnWay': 'В пути',
'Delivered': 'Выполнен', 'Delivered': 'Выполнен',
'Closed': 'Выполнен', 'Closed': 'Выполнен',
}; };
export const lvlPeriods: lvlPeriod[] = [ export const lvlPeriods: lvlPeriod[] = [
{ {
percent: 3, percent: 3,
start: 0, start: 0,
end: 1600, end: 1600,
color: '#f2c94c' color: '#f2c94c'
}, },
{ {
percent: 6, percent: 6,
start: 1601, start: 1601,
end: 3600, end: 3600,
color: '#f2994a' color: '#f2994a'
}, },
{ {
percent: 10, percent: 10,
start: 3601, start: 3601,
end: 8600, end: 8600,
color: '#6fcf97' color: '#6fcf97'
}, },
{ {
percent: 15, percent: 15,
start: 8601, start: 8601,
color: '#6fcf97' color: '#6fcf97'
}, },
] ]

View File

@ -1,121 +1,121 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { LoyalityProgramComponent } from './pages/loyality-program/loyality-program.component'; import { LoyalityProgramComponent } from './pages/loyality-program/loyality-program.component';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { MainComponent } from './pages/main/main.component'; import { MainComponent } from './pages/main/main.component';
import { NavbarComponent } from './components/navbar/navbar.component'; import { NavbarComponent } from './components/navbar/navbar.component';
import { CardComponent } from './components/card/card.component'; import { CardComponent } from './components/card/card.component';
import { InputMaskModule } from 'primeng/inputmask'; import { InputMaskModule } from 'primeng/inputmask';
import { AuthComponent } from './pages/account/auth/auth.component'; import { AuthComponent } from './pages/account/auth/auth.component';
import { ProgressSpinnerModule } from 'primeng/progressspinner'; import { ProgressSpinnerModule } from 'primeng/progressspinner';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AccountComponent } from './pages/account/account.component'; import { AccountComponent } from './pages/account/account.component';
import { ExitComponent } from './components/exit/exit.component'; import { ExitComponent } from './components/exit/exit.component';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { DialogService } from 'primeng/dynamicdialog'; import { DialogService } from 'primeng/dynamicdialog';
import { BonusProgramComponent } from './pages/account/bonus-program/bonus-program.component'; import { BonusProgramComponent } from './pages/account/bonus-program/bonus-program.component';
import { OrderInfoComponent } from './components/order-info/order-info.component'; import { OrderInfoComponent } from './components/order-info/order-info.component';
import { ServiceWorkerModule } from '@angular/service-worker'; import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment'; import { environment } from '../environments/environment';
import { AngularFireModule } from '@angular/fire/compat'; import { AngularFireModule } from '@angular/fire/compat';
import { AngularFireMessagingModule } from '@angular/fire/compat/messaging'; import { AngularFireMessagingModule } from '@angular/fire/compat/messaging';
import { ToastModule } from 'primeng/toast'; import { ToastModule } from 'primeng/toast';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { FooterButtonsComponent } from './components/footer-buttons/footer-buttons.component'; import { FooterButtonsComponent } from './components/footer-buttons/footer-buttons.component';
import { UserDataComponent } from './pages/account/user-data/user-data.component'; import { UserDataComponent } from './pages/account/user-data/user-data.component';
import { RefSystemComponent } from './pages/account/ref-system/ref-system.component'; import { RefSystemComponent } from './pages/account/ref-system/ref-system.component';
import { ShareButtonsModule } from 'ngx-sharebuttons/buttons'; import { ShareButtonsModule } from 'ngx-sharebuttons/buttons';
import { ShareIconsModule } from 'ngx-sharebuttons/icons'; import { ShareIconsModule } from 'ngx-sharebuttons/icons';
import { MessagingService } from './services/messaging.service'; import { MessagingService } from './services/messaging.service';
import { NotFoundComponent } from './pages/not-found/not-found.component'; import { NotFoundComponent } from './pages/not-found/not-found.component';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { GuestCardComponent } from './pages/guest-card/guest-card.component'; import { GuestCardComponent } from './pages/guest-card/guest-card.component';
import { QrCodeModule } from 'ng-qrcode'; import { QrCodeModule } from 'ng-qrcode';
import { AccordionComponent } from './components/accordion/accordion.component'; import { AccordionComponent } from './components/accordion/accordion.component';
import { LastOrderComponent } from './components/last-order/last-order.component'; import { LastOrderComponent } from './components/last-order/last-order.component';
import { InviteFriendsComponent } from './components/invite-friends/invite-friends.component'; import { InviteFriendsComponent } from './components/invite-friends/invite-friends.component';
import { FooterComponent } from './components/footer/footer.component'; import { FooterComponent } from './components/footer/footer.component';
import { SocialMediaButtonsComponent } from './components/social-media-buttons/social-media-buttons.component'; import { SocialMediaButtonsComponent } from './components/social-media-buttons/social-media-buttons.component';
import { LoginComponent } from './pages/login/login.component'; import { LoginComponent } from './pages/login/login.component';
import { NgxMatIntlTelInputComponent } from 'ngx-mat-intl-tel-input'; import { NgxMatIntlTelInputComponent } from 'ngx-mat-intl-tel-input';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatSnackBarModule } from '@angular/material/snack-bar';
import { import {
MAT_BOTTOM_SHEET_DATA, MAT_BOTTOM_SHEET_DATA,
MatBottomSheetModule, MatBottomSheetModule,
MatBottomSheetRef, MatBottomSheetRef,
} from '@angular/material/bottom-sheet'; } from '@angular/material/bottom-sheet';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { DirectivesModule } from './directives/directives.module'; import { DirectivesModule } from './directives/directives.module';
@NgModule({ @NgModule({
declarations: [ declarations: [
AppComponent, AppComponent,
NavbarComponent, NavbarComponent,
MainComponent, MainComponent,
CardComponent, CardComponent,
AuthComponent, AuthComponent,
AccountComponent, AccountComponent,
ExitComponent, ExitComponent,
BonusProgramComponent, BonusProgramComponent,
OrderInfoComponent, OrderInfoComponent,
FooterButtonsComponent, FooterButtonsComponent,
UserDataComponent, UserDataComponent,
RefSystemComponent, RefSystemComponent,
NotFoundComponent, NotFoundComponent,
GuestCardComponent, GuestCardComponent,
AccordionComponent, AccordionComponent,
LastOrderComponent, LastOrderComponent,
InviteFriendsComponent, InviteFriendsComponent,
FooterComponent, FooterComponent,
SocialMediaButtonsComponent, SocialMediaButtonsComponent,
LoginComponent, LoginComponent,
// FocusNextInputDirective, // FocusNextInputDirective,
LoyalityProgramComponent, LoyalityProgramComponent,
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
AppRoutingModule, AppRoutingModule,
InputMaskModule, InputMaskModule,
ProgressSpinnerModule, ProgressSpinnerModule,
FormsModule, FormsModule,
HttpClientModule, HttpClientModule,
BrowserAnimationsModule, BrowserAnimationsModule,
BrowserModule, BrowserModule,
ServiceWorkerModule.register('/sw-master.js', { ServiceWorkerModule.register('/sw-master.js', {
enabled: environment.production, enabled: environment.production,
// Register the ServiceWorker as soon as the application is stable // Register the ServiceWorker as soon as the application is stable
// or after 30 seconds (whichever comes first). // or after 30 seconds (whichever comes first).
registrationStrategy: 'registerWhenStable:30000', registrationStrategy: 'registerWhenStable:30000',
}), }),
AngularFireModule.initializeApp(environment.firebase), AngularFireModule.initializeApp(environment.firebase),
AngularFireMessagingModule, AngularFireMessagingModule,
ToastModule, ToastModule,
ReactiveFormsModule, ReactiveFormsModule,
ShareButtonsModule.withConfig({ ShareButtonsModule.withConfig({
debug: true, debug: true,
}), }),
ShareIconsModule, ShareIconsModule,
MatIconModule, MatIconModule,
QrCodeModule, QrCodeModule,
NgxMatIntlTelInputComponent, NgxMatIntlTelInputComponent,
MatFormFieldModule, MatFormFieldModule,
MatInputModule, MatInputModule,
MatSnackBarModule, MatSnackBarModule,
MatBottomSheetModule, MatBottomSheetModule,
MatProgressSpinnerModule, MatProgressSpinnerModule,
DirectivesModule, DirectivesModule,
], ],
providers: [ providers: [
DialogService, DialogService,
MessageService, MessageService,
MessagingService, MessagingService,
{ provide: MatBottomSheetRef, useValue: {} }, { provide: MatBottomSheetRef, useValue: {} },
{ provide: MAT_BOTTOM_SHEET_DATA, useValue: {} }, { provide: MAT_BOTTOM_SHEET_DATA, useValue: {} },
], ],
bootstrap: [AppComponent], bootstrap: [AppComponent],
}) })
export class AppModule {} export class AppModule {}

View File

@ -1,7 +1,7 @@
<div class="tab"> <div class="tab">
<input [id]="'tab-' + guid" type="checkbox" name="tabs" /> <input [id]="'tab-' + guid" type="checkbox" name="tabs" />
<label [for]="'tab-' + guid">{{ header }}</label> <label [for]="'tab-' + guid">{{ header }}</label>
<div class="tab-content"> <div class="tab-content">
<ng-content></ng-content> <ng-content></ng-content>
</div> </div>
</div> </div>

View File

@ -1,85 +1,85 @@
:host { :host {
width: 100%; width: 100%;
} }
.tab { .tab {
position: relative; position: relative;
margin-bottom: 1px; margin-bottom: 1px;
width: 100%; width: 100%;
color: var(--text-color); color: var(--text-color);
overflow: hidden; overflow: hidden;
border-bottom: solid 1px #bdbdbd; border-bottom: solid 1px #bdbdbd;
} }
.tab input { .tab input {
position: absolute; position: absolute;
opacity: 0; opacity: 0;
z-index: -1; z-index: -1;
} }
.tab label { .tab label {
display: block; display: block;
padding: 20px 26px; padding: 20px 26px;
position: relative; position: relative;
background: #ffffff00; background: #ffffff00;
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 14px; line-height: 14px;
letter-spacing: -0.5px; letter-spacing: -0.5px;
cursor: pointer; cursor: pointer;
} }
// .tab label:before { // .tab label:before {
// display: block; // display: block;
// content: " "; // content: " ";
// padding-top: 5px; // padding-top: 5px;
// } // }
.tab-content { .tab-content {
max-height: 0; max-height: 0;
overflow: hidden; overflow: hidden;
background: #292929; background: #292929;
-webkit-transition: all 0.35s; -webkit-transition: all 0.35s;
-o-transition: all 0.35s; -o-transition: all 0.35s;
transition: all 0.35s; transition: all 0.35s;
border-top: solid 1px #bdbdbd; border-top: solid 1px #bdbdbd;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
p { p {
margin: 0; margin: 0;
} }
} }
.tab-content p { .tab-content p {
margin: 1em; margin: 1em;
} }
/* :checked */ /* :checked */
.tab input:checked ~ .tab-content { .tab input:checked ~ .tab-content {
padding: 16px 26px; padding: 16px 26px;
max-height: 100vh; max-height: 100vh;
} }
/* Icon */ /* Icon */
.tab label::after { .tab label::after {
position: absolute; position: absolute;
right: 0; right: 0;
top: 0; top: 0;
line-height: 3; line-height: 3;
text-align: center; text-align: center;
-webkit-transition: all 0.35s; -webkit-transition: all 0.35s;
-o-transition: all 0.35s; -o-transition: all 0.35s;
transition: all 0.35s; transition: all 0.35s;
height: 100%; height: 100%;
margin-right: 12px; margin-right: 12px;
display: flex; display: flex;
align-items: center; align-items: center;
} }
.tab input[type="checkbox"] + label::after { .tab input[type="checkbox"] + label::after {
content: "+"; content: "+";
} }
.tab input[type="radio"] + label::after { .tab input[type="radio"] + label::after {
content: "\25BC"; content: "\25BC";
} }
.tab input[type="checkbox"]:checked + label::after { .tab input[type="checkbox"]:checked + label::after {
transform: rotate(315deg); transform: rotate(315deg);
} }
.tab input[type="radio"]:checked + label::after { .tab input[type="radio"]:checked + label::after {
transform: rotateX(180deg); transform: rotateX(180deg);
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AccordionComponent } from './accordion.component'; import { AccordionComponent } from './accordion.component';
describe('AccordionComponent', () => { describe('AccordionComponent', () => {
let component: AccordionComponent; let component: AccordionComponent;
let fixture: ComponentFixture<AccordionComponent>; let fixture: ComponentFixture<AccordionComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ AccordionComponent ] declarations: [ AccordionComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(AccordionComponent); fixture = TestBed.createComponent(AccordionComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,23 +1,23 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
export interface IAccordionData { export interface IAccordionData {
header: string; header: string;
body: string; body: string;
} }
@Component({ @Component({
selector: 'app-accordion[header]', selector: 'app-accordion[header]',
templateUrl: './accordion.component.html', templateUrl: './accordion.component.html',
styleUrls: ['./accordion.component.scss'] styleUrls: ['./accordion.component.scss']
}) })
export class AccordionComponent implements OnInit { export class AccordionComponent implements OnInit {
@Input() header!: string @Input() header!: string
public guid: string = uuidv4() public guid: string = uuidv4()
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {
} }
} }

View File

@ -1 +1 @@
<p>card works!</p> <p>card works!</p>

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CardComponent } from './card.component'; import { CardComponent } from './card.component';
describe('CardComponent', () => { describe('CardComponent', () => {
let component: CardComponent; let component: CardComponent;
let fixture: ComponentFixture<CardComponent>; let fixture: ComponentFixture<CardComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ CardComponent ] declarations: [ CardComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(CardComponent); fixture = TestBed.createComponent(CardComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,15 +1,15 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
@Component({ @Component({
selector: 'app-card', selector: 'app-card',
templateUrl: './card.component.html', templateUrl: './card.component.html',
styleUrls: ['./card.component.scss'] styleUrls: ['./card.component.scss']
}) })
export class CardComponent implements OnInit { export class CardComponent implements OnInit {
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {
} }
} }

View File

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

View File

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

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ExitComponent } from './exit.component'; import { ExitComponent } from './exit.component';
describe('ExitComponent', () => { describe('ExitComponent', () => {
let component: ExitComponent; let component: ExitComponent;
let fixture: ComponentFixture<ExitComponent>; let fixture: ComponentFixture<ExitComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ ExitComponent ] declarations: [ ExitComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(ExitComponent); fixture = TestBed.createComponent(ExitComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

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

View File

@ -1,17 +1,17 @@
<div class="footer-buttons-container"> <div class="footer-buttons-container">
<!-- <button <!-- <button
*ngIf="((deviceType == 'android' && deferredPrompt) || deviceType == 'ios') && token?.length" *ngIf="((deviceType == 'android' && deferredPrompt) || deviceType == 'ios') && token?.length"
class="footer-buttons-container__button download" class="footer-buttons-container__button download"
(click)="downloadPWA()" (click)="downloadPWA()"
> >
<img src="./assets/download.svg" alt="download" /> <img src="./assets/download.svg" alt="download" />
</button> --> </button> -->
<button <button
*ngIf="!isPermissionNotifications && token?.length" *ngIf="!isPermissionNotifications && token?.length"
class="footer-buttons-container__button" class="footer-buttons-container__button"
(click)="requestPermission()" (click)="requestPermission()"
value="click to copy" value="click to copy"
> >
<img src="./assets/notification.svg" alt="notification" /> <img src="./assets/notification.svg" alt="notification" />
</button> </button>
</div> </div>

View File

@ -1,39 +1,39 @@
:host { :host {
.footer-buttons-container { .footer-buttons-container {
position: fixed; position: fixed;
bottom: 16px; bottom: 16px;
right: 0; right: 0;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
z-index: 9999; z-index: 9999;
flex-direction: column; flex-direction: column;
align-items: flex-end; align-items: flex-end;
padding: 0 8px; padding: 0 8px;
&__button { &__button {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 60px; width: 60px;
height: 60px; height: 60px;
background: var(--text-color); background: var(--text-color);
border: solid var(--text-color) 1px !important; border: solid var(--text-color) 1px !important;
color: var(--text-color); color: var(--text-color);
border-radius: 100%; border-radius: 100%;
font-weight: 600; font-weight: 600;
letter-spacing: 2px; letter-spacing: 2px;
border: none; border: none;
margin-top: 8px; margin-top: 8px;
margin-right: 0; margin-right: 0;
cursor: pointer; cursor: pointer;
img { img {
width: 60%; width: 60%;
} }
} }
} }
@media (display-mode: standalone) { @media (display-mode: standalone) {
.download { .download {
display: none; display: none;
} }
} }
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FooterButtonsComponent } from './footer-buttons.component'; import { FooterButtonsComponent } from './footer-buttons.component';
describe('FooterButtonsComponent', () => { describe('FooterButtonsComponent', () => {
let component: FooterButtonsComponent; let component: FooterButtonsComponent;
let fixture: ComponentFixture<FooterButtonsComponent>; let fixture: ComponentFixture<FooterButtonsComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ FooterButtonsComponent ] declarations: [ FooterButtonsComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(FooterButtonsComponent); fixture = TestBed.createComponent(FooterButtonsComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,24 +1,24 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
@Component({ @Component({
selector: 'app-footer-buttons', selector: 'app-footer-buttons',
templateUrl: './footer-buttons.component.html', templateUrl: './footer-buttons.component.html',
styleUrls: ['./footer-buttons.component.scss'] styleUrls: ['./footer-buttons.component.scss']
}) })
export class FooterButtonsComponent implements OnInit { export class FooterButtonsComponent implements OnInit {
@Input() token!: string; @Input() token!: string;
@Input() isPermissionNotifications!: boolean; @Input() isPermissionNotifications!: boolean;
@Output() requestingPermission = new EventEmitter<null>(); @Output() requestingPermission = new EventEmitter<null>();
public deviceType: 'ios' | 'android' | null = null; public deviceType: 'ios' | 'android' | null = null;
constructor( constructor(
) { } ) { }
ngOnInit(): void { ngOnInit(): void {
} }
requestPermission() { requestPermission() {
this.requestingPermission.emit(null) this.requestingPermission.emit(null)
} }
} }

View File

@ -1,4 +1,4 @@
<img src="/assets/logo.svg" alt="Логотип"> <img src="/assets/logo.svg" alt="Логотип">
<h3>Горячая линия</h3> <h3>Горячая линия</h3>
<a class="phone-number" href="tel:88003334130">8 (800) 333-41-30</a> <a class="phone-number" href="tel:88003334130">8 (800) 333-41-30</a>
<app-social-media-buttons></app-social-media-buttons> <app-social-media-buttons></app-social-media-buttons>

View File

@ -1,28 +1,28 @@
:host { :host {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 20px; gap: 20px;
padding-bottom: 100px; padding-bottom: 100px;
h3 { h3 {
margin: 0; margin: 0;
font-family: Montserrat; font-family: Montserrat;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 15px; font-size: 15px;
line-height: 20px; line-height: 20px;
text-align: center; text-align: center;
letter-spacing: -0.24px; letter-spacing: -0.24px;
color: #6a737c; color: #6a737c;
} }
.phone-number { .phone-number {
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 17px; font-size: 17px;
line-height: 22px; line-height: 22px;
text-align: center; text-align: center;
letter-spacing: -0.408px; letter-spacing: -0.408px;
color: #d9d9d9; color: #d9d9d9;
text-decoration: none; text-decoration: none;
} }
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FooterComponent } from './footer.component'; import { FooterComponent } from './footer.component';
describe('FooterComponent', () => { describe('FooterComponent', () => {
let component: FooterComponent; let component: FooterComponent;
let fixture: ComponentFixture<FooterComponent>; let fixture: ComponentFixture<FooterComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ FooterComponent ] declarations: [ FooterComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(FooterComponent); fixture = TestBed.createComponent(FooterComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,15 +1,15 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
@Component({ @Component({
selector: 'app-footer', selector: 'app-footer',
templateUrl: './footer.component.html', templateUrl: './footer.component.html',
styleUrls: ['./footer.component.scss'] styleUrls: ['./footer.component.scss']
}) })
export class FooterComponent implements OnInit { export class FooterComponent implements OnInit {
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {
} }
} }

View File

@ -1,25 +1,25 @@
<h2>Пригласи друзей!</h2> <h2>Пригласи друзей!</h2>
<div class="container"> <div class="container">
<p> <p>
Пригласи друзей зарегистрироваться в приложении по твоему уникальному коду и Пригласи друзей зарегистрироваться в приложении по твоему уникальному коду и
получи бонусы, когда они совершат первую покупку. получи бонусы, когда они совершат первую покупку.
</p> </p>
<button class="share" (click)="share()" [disabled]="loading"> <button class="share" (click)="share()" [disabled]="loading">
<ng-container *ngIf="loading"> <ng-container *ngIf="loading">
<ng-container <ng-container
*ngTemplateOutlet="spinner; context: { $implicit: 24 }" *ngTemplateOutlet="spinner; context: { $implicit: 24 }"
></ng-container> ></ng-container>
</ng-container> </ng-container>
<mat-icon <mat-icon
*ngIf="!loading" *ngIf="!loading"
aria-hidden="false" aria-hidden="false"
aria-label="Share" aria-label="Share"
fontIcon="share" fontIcon="share"
style="color: #fff" style="color: #fff"
></mat-icon> ></mat-icon>
</button> </button>
</div> </div>
<ng-template #spinner let-diameter> <ng-template #spinner let-diameter>
<mat-spinner [strokeWidth]="3" [diameter]="diameter"></mat-spinner> <mat-spinner [strokeWidth]="3" [diameter]="diameter"></mat-spinner>
</ng-template> </ng-template>

View File

@ -1,34 +1,34 @@
:host { :host {
padding: 16px; padding: 16px;
h2 { h2 {
font-family: Montserrat; font-family: Montserrat;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 17px; font-size: 17px;
line-height: 22px; line-height: 22px;
letter-spacing: -0.408px; letter-spacing: -0.408px;
} }
& .container { & .container {
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
gap: 16px; gap: 16px;
p { p {
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 16px; line-height: 16px;
color: var(--text-color); color: var(--text-color);
} }
.share { .share {
width: 72px; width: 72px;
height: 48px; height: 48px;
background: var(--button-color); background: var(--button-color);
padding: 8px 26px; padding: 8px 26px;
border: none; border: none;
} }
} }
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InviteFriendsComponent } from './invite-friends.component'; import { InviteFriendsComponent } from './invite-friends.component';
describe('InviteFriendsComponent', () => { describe('InviteFriendsComponent', () => {
let component: InviteFriendsComponent; let component: InviteFriendsComponent;
let fixture: ComponentFixture<InviteFriendsComponent>; let fixture: ComponentFixture<InviteFriendsComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ InviteFriendsComponent ] declarations: [ InviteFriendsComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(InviteFriendsComponent); fixture = TestBed.createComponent(InviteFriendsComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,54 +1,54 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { lastValueFrom } from 'rxjs'; import { lastValueFrom } from 'rxjs';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
@Component({ @Component({
selector: 'app-invite-friends', selector: 'app-invite-friends',
templateUrl: './invite-friends.component.html', templateUrl: './invite-friends.component.html',
styleUrls: ['./invite-friends.component.scss'] styleUrls: ['./invite-friends.component.scss']
}) })
export class InviteFriendsComponent implements OnInit { export class InviteFriendsComponent implements OnInit {
public refUrl: string = `${environment.defaultUrl}/?refUserId=` public refUrl: string = `${environment.defaultUrl}/?refUserId=`
public loading: boolean = true; public loading: boolean = true;
private shareData: ShareData = { private shareData: ShareData = {
title: '' title: ''
} }
constructor( constructor(
private jsonrpc: JsonrpcService, private jsonrpc: JsonrpcService,
private messageService: MessageService private messageService: MessageService
) { } ) { }
async ngOnInit() { async ngOnInit() {
const accountData = (await lastValueFrom( const accountData = (await lastValueFrom(
this.jsonrpc this.jsonrpc
.rpc( .rpc(
{ {
method: 'getTokenData', method: 'getTokenData',
params: [], params: [],
}, },
RpcService.authService, RpcService.authService,
true true
) )
)).data )).data
this.refUrl += accountData.user_id this.refUrl += accountData.user_id
this.loading = false this.loading = false
} }
share() { share() {
if (navigator.share) { if (navigator.share) {
navigator.share({ navigator.share({
title: document.title, title: document.title,
text: "Tasty Coffee", text: "Tasty Coffee",
url: this.refUrl url: this.refUrl
}) })
.then(() => console.log('Successful share')) .then(() => console.log('Successful share'))
.catch((error) => { .catch((error) => {
console.log('Error sharing:', error) console.log('Error sharing:', error)
}); });
} }
} }
} }

View File

@ -1,31 +1,31 @@
<h2>Ваш предыдущий заказ</h2> <h2>Ваш предыдущий заказ</h2>
<div class="info-order"> <div class="info-order">
<p class="flex"><span>Дата: </span> <p class="flex"><span>Дата: </span>
<span *ngIf="!loading">{{(lastOrder?.transactionCreateDate | date:'dd.MM.yyyyг.') || 'Данные не найдены'}}</span> <span *ngIf="!loading">{{(lastOrder?.transactionCreateDate | date:'dd.MM.yyyyг.') || 'Данные не найдены'}}</span>
<ng-container *ngIf="loading"> <ng-container *ngIf="loading">
<ng-container <ng-container
*ngTemplateOutlet="spinner; context: { $implicit: 24 }" *ngTemplateOutlet="spinner; context: { $implicit: 24 }"
></ng-container> ></ng-container>
</ng-container> </ng-container>
</p> </p>
<p class="flex"><span>На сумму: </span> <p class="flex"><span>На сумму: </span>
<span *ngIf="!loading">{{lastOrder?.orderSum ? lastOrder?.orderSum + ' ₽' : 'Данные не найдены'}}</span> <span *ngIf="!loading">{{lastOrder?.orderSum ? lastOrder?.orderSum + ' ₽' : 'Данные не найдены'}}</span>
<ng-container *ngIf="loading"> <ng-container *ngIf="loading">
<ng-container <ng-container
*ngTemplateOutlet="spinner; context: { $implicit: 24 }" *ngTemplateOutlet="spinner; context: { $implicit: 24 }"
></ng-container> ></ng-container>
</ng-container> </ng-container>
</p> </p>
</div> </div>
<a href="https://yandex.ru/profile/151770398186" target="_blank"> <a href="https://yandex.ru/profile/151770398186" target="_blank">
<button class="evaluate-order">Оценить заказ</button> <button class="evaluate-order">Оценить заказ</button>
</a> </a>
<p class="info"> <p class="info">
Списание бонусов возможно на любые категории. Бонусами можно оплатить 100% Списание бонусов возможно на любые категории. Бонусами можно оплатить 100%
суммы покупки. Бонусы начисляются только на напитки с учётом добавок. суммы покупки. Бонусы начисляются только на напитки с учётом добавок.
Неиспользованные бонусы сгорают в течение 90 дней. Неиспользованные бонусы сгорают в течение 90 дней.
</p> </p>
<ng-template #spinner let-diameter> <ng-template #spinner let-diameter>
<mat-spinner [strokeWidth]="3" [diameter]="diameter"></mat-spinner> <mat-spinner [strokeWidth]="3" [diameter]="diameter"></mat-spinner>
</ng-template> </ng-template>

View File

@ -1,52 +1,52 @@
:host { :host {
padding: 24px 16px 0px; padding: 24px 16px 0px;
& > h2 { & > h2 {
font-family: Montserrat; font-family: Montserrat;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 15px; font-size: 15px;
line-height: 20px; line-height: 20px;
letter-spacing: -0.24px; letter-spacing: -0.24px;
} }
& > .info-order { & > .info-order {
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 16px; line-height: 16px;
span { span {
color: #828282; color: #828282;
} }
.flex { .flex {
display: flex; display: flex;
align-items: center; align-items: center;
flex-direction: row; flex-direction: row;
gap: 8px; gap: 8px;
} }
} }
.evaluate-order { .evaluate-order {
margin: 24px 0; margin: 24px 0;
width: 100%; width: 100%;
padding: 12px; padding: 12px;
text-align: center; text-align: center;
border: 2px solid #28af49; border: 2px solid #28af49;
border-radius: 6px; border-radius: 6px;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 17px; font-size: 17px;
line-height: 22px; line-height: 22px;
letter-spacing: -0.408px; letter-spacing: -0.408px;
background-color: transparent; background-color: transparent;
color: #28af49; color: #28af49;
} }
.info { .info {
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 16px; line-height: 16px;
color: rgba(255, 255, 255, 0.5); color: rgba(255, 255, 255, 0.5);
} }
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LastOrderComponent } from './last-order.component'; import { LastOrderComponent } from './last-order.component';
describe('LastOrderComponent', () => { describe('LastOrderComponent', () => {
let component: LastOrderComponent; let component: LastOrderComponent;
let fixture: ComponentFixture<LastOrderComponent>; let fixture: ComponentFixture<LastOrderComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ LastOrderComponent ] declarations: [ LastOrderComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(LastOrderComponent); fixture = TestBed.createComponent(LastOrderComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,18 +1,18 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { Purchase } from 'src/app/interface/data'; import { Purchase } from 'src/app/interface/data';
@Component({ @Component({
selector: 'app-last-order[lastOrder]', selector: 'app-last-order[lastOrder]',
templateUrl: './last-order.component.html', templateUrl: './last-order.component.html',
styleUrls: ['./last-order.component.scss'] styleUrls: ['./last-order.component.scss']
}) })
export class LastOrderComponent implements OnInit { export class LastOrderComponent implements OnInit {
@Input() lastOrder!: Purchase; @Input() lastOrder!: Purchase;
@Input() loading!: boolean; @Input() loading!: boolean;
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {
} }
} }

View File

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

View File

@ -1,33 +1,33 @@
:host { :host {
width: 100%; width: 100%;
} }
.container { .container {
box-sizing: border-box; box-sizing: border-box;
padding: 12px 16px; padding: 12px 16px;
width: 100%; width: 100%;
background: #231f20; background: #231f20;
box-shadow: 0px 8px 16px rgba(17, 17, 17, 0.5); box-shadow: 0px 8px 16px rgba(17, 17, 17, 0.5);
color: var(--text-color); color: var(--text-color);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
.back-arrow { .back-arrow {
font-size: 16px; font-size: 16px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.plug { .plug {
height: 24px; height: 24px;
width: 24px; width: 24px;
visibility: hidden; visibility: hidden;
} }
.title { .title {
font-family: "Montserrat", sans-serif; font-family: "Montserrat", sans-serif;
font-weight: 700; font-weight: 700;
font-size: 17px; font-size: 17px;
line-height: 22px; line-height: 22px;
margin: 0; margin: 0;
} }
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NavbarComponent } from './navbar.component'; import { NavbarComponent } from './navbar.component';
describe('NavbarComponent', () => { describe('NavbarComponent', () => {
let component: NavbarComponent; let component: NavbarComponent;
let fixture: ComponentFixture<NavbarComponent>; let fixture: ComponentFixture<NavbarComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ NavbarComponent ] declarations: [ NavbarComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(NavbarComponent); fixture = TestBed.createComponent(NavbarComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,97 +1,97 @@
.woocommerce-MyAccount-content { .woocommerce-MyAccount-content {
max-width: 400px; max-width: 400px;
margin-left: calc(50vw - 200px); margin-left: calc(50vw - 200px);
} }
.container-progressbar { .container-progressbar {
width: 80%; width: 80%;
transform: translate(0, 0); transform: translate(0, 0);
margin: 18px 10% 90px; margin: 18px 10% 90px;
} }
.steps { .steps {
position: relative; position: relative;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
width: 100%; width: 100%;
} }
.step { .step {
width: 20px; width: 20px;
height: 20px; height: 20px;
border: 2px solid #acaca6; border: 2px solid #acaca6;
border-radius: 50%; border-radius: 50%;
transition: background 1s; transition: background 1s;
position: relative; position: relative;
.status-image { .status-image {
width: 80px; width: 80px;
height: 80px; height: 80px;
position: absolute; position: absolute;
top: 25px; top: 25px;
left: -32px; left: -32px;
} }
} }
.step.selected { .step.selected {
border: 2px solid #f9b004; border: 2px solid #f9b004;
} }
.step.completed { .step.completed {
border: 2px solid #f9b004; border: 2px solid #f9b004;
background: #f9b004; background: #f9b004;
} }
.step.cancelled { .step.cancelled {
background: #d7120b; background: #d7120b;
border: 2px solid #d7120b; border: 2px solid #d7120b;
} }
.progress { .progress {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 50%; height: 50%;
border-bottom: 2px solid #acaca6; border-bottom: 2px solid #acaca6;
z-index: -1; z-index: -1;
} }
.percent { .percent {
position: absolute; position: absolute;
width: 0; width: 0;
top: 2px; top: 2px;
height: 100%; height: 100%;
border-bottom: 2px solid #f9b004; border-bottom: 2px solid #f9b004;
z-index: 1; z-index: 1;
transition: width 1s; transition: width 1s;
} }
p { p {
margin: 16px 0; margin: 16px 0;
} }
.woocommerce-order-details { .woocommerce-order-details {
&__title { &__title {
font-weight: 600; font-weight: 600;
font-size: 18px; font-size: 18px;
} }
} }
.woocommerce-table { .woocommerce-table {
border: 2px solid #dee2e6; border: 2px solid #dee2e6;
border-radius: 0.25rem; border-radius: 0.25rem;
border-collapse: separate; border-collapse: separate;
text-align: left; text-align: left;
margin-top: 8px; margin-top: 8px;
th { th {
font-weight: 600; font-weight: 600;
} }
th, td { th, td {
padding: 0.7rem 1.5rem; padding: 0.7rem 1.5rem;
border-bottom: 2px solid #dee2e6; border-bottom: 2px solid #dee2e6;
} }
} }
.woocommerce-column { .woocommerce-column {
&__title { &__title {
margin-top: 16px; margin-top: 16px;
font-weight: 600; font-weight: 600;
font-size: 18px; font-size: 18px;
} }
} }
address { address {
border: 1px solid #dee2e6; border: 1px solid #dee2e6;
border-bottom-width: 2px; border-bottom-width: 2px;
border-right-width: 2px; border-right-width: 2px;
text-align: left; text-align: left;
width: 100%; width: 100%;
border-radius: 5px; border-radius: 5px;
padding: 10px 12px; padding: 10px 12px;
margin-top: 8px; margin-top: 8px;
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OrderInfoComponent } from './order-info.component'; import { OrderInfoComponent } from './order-info.component';
describe('OrderInfoComponent', () => { describe('OrderInfoComponent', () => {
let component: OrderInfoComponent; let component: OrderInfoComponent;
let fixture: ComponentFixture<OrderInfoComponent>; let fixture: ComponentFixture<OrderInfoComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ OrderInfoComponent ] declarations: [ OrderInfoComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(OrderInfoComponent); fixture = TestBed.createComponent(OrderInfoComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,43 +1,43 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { orderStatuses } from 'src/app/app.constants'; import { orderStatuses } from 'src/app/app.constants';
import { AcceptedOrder, OrderProduct } from 'src/app/interface/data'; import { AcceptedOrder, OrderProduct } from 'src/app/interface/data';
import * as moment from 'moment-timezone'; import * as moment from 'moment-timezone';
@Component({ @Component({
selector: 'app-order-info', selector: 'app-order-info',
templateUrl: './order-info.component.html', templateUrl: './order-info.component.html',
styleUrls: ['./order-info.component.scss'] styleUrls: ['./order-info.component.scss']
}) })
export class OrderInfoComponent implements OnInit { export class OrderInfoComponent implements OnInit {
@Input() order!: AcceptedOrder; @Input() order!: AcceptedOrder;
@Input() loadingStatus: boolean = false; @Input() loadingStatus: boolean = false;
percent: number = 0; percent: number = 0;
readonly moment = moment; readonly moment = moment;
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {
this.setPercent(); this.setPercent();
} }
setPercent() { setPercent() {
const statusIndex = this.getStatusIndex(this.order.status); const statusIndex = this.getStatusIndex(this.order.status);
if (statusIndex !== -1 && statusIndex !== 0) { if (statusIndex !== -1 && statusIndex !== 0) {
this.percent = (statusIndex - 1) * 20; this.percent = (statusIndex - 1) * 20;
} }
} }
formatStatus(status: string): string|undefined{ formatStatus(status: string): string|undefined{
const key = Object.keys(orderStatuses).find((el) => status === el) ?? ''; const key = Object.keys(orderStatuses).find((el) => status === el) ?? '';
return orderStatuses[key]; return orderStatuses[key];
} }
getStatusIndex(status: string) { getStatusIndex(status: string) {
return [...new Set(Object.values(orderStatuses))].findIndex((value) => value === this.formatStatus(status)); return [...new Set(Object.values(orderStatuses))].findIndex((value) => value === this.formatStatus(status));
} }
getItemModifiersName(item: OrderProduct): string[]{ getItemModifiersName(item: OrderProduct): string[]{
return Object.keys(item.modifiers); return Object.keys(item.modifiers);
} }
} }

View File

@ -1,3 +1,3 @@
<a *ngFor="let link of links" [href]="link.url" target="_blank"> <a *ngFor="let link of links" [href]="link.url" target="_blank">
<img [src]="link.imgUrl" [alt]="link.label" /> <img [src]="link.imgUrl" [alt]="link.label" />
</a> </a>

View File

@ -1,15 +1,15 @@
:host { :host {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
gap: 16px; gap: 16px;
justify-content: space-between; justify-content: space-between;
a { a {
width: 48px; width: 48px;
height: 48px; height: 48px;
border-radius: 100%; border-radius: 100%;
background: #333333; background: #333333;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SocialMediaButtonsComponent } from './social-media-buttons.component'; import { SocialMediaButtonsComponent } from './social-media-buttons.component';
describe('SocialMediaButtonsComponent', () => { describe('SocialMediaButtonsComponent', () => {
let component: SocialMediaButtonsComponent; let component: SocialMediaButtonsComponent;
let fixture: ComponentFixture<SocialMediaButtonsComponent>; let fixture: ComponentFixture<SocialMediaButtonsComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ SocialMediaButtonsComponent ] declarations: [ SocialMediaButtonsComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(SocialMediaButtonsComponent); fixture = TestBed.createComponent(SocialMediaButtonsComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,38 +1,38 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
export interface ISocialMediaLink { export interface ISocialMediaLink {
url: string; url: string;
imgUrl: string; imgUrl: string;
label: string; label: string;
} }
@Component({ @Component({
selector: 'app-social-media-buttons', selector: 'app-social-media-buttons',
templateUrl: './social-media-buttons.component.html', templateUrl: './social-media-buttons.component.html',
styleUrls: ['./social-media-buttons.component.scss'] styleUrls: ['./social-media-buttons.component.scss']
}) })
export class SocialMediaButtonsComponent implements OnInit { export class SocialMediaButtonsComponent implements OnInit {
public links: ISocialMediaLink[] = [ public links: ISocialMediaLink[] = [
{ {
label: 'Инстаграм', label: 'Инстаграм',
url: 'https://www.instagram.com/', url: 'https://www.instagram.com/',
imgUrl: '/assets/social-media-icons/instagram.svg' imgUrl: '/assets/social-media-icons/instagram.svg'
}, },
{ {
label: 'ВК', label: 'ВК',
url: 'https://vk.com/coffeelike_com', url: 'https://vk.com/coffeelike_com',
imgUrl: '/assets/social-media-icons/vk.svg' imgUrl: '/assets/social-media-icons/vk.svg'
}, },
{ {
label: 'Youtube', label: 'Youtube',
url: 'https://www.youtube.com/c/coffeelikeru', url: 'https://www.youtube.com/c/coffeelikeru',
imgUrl: '/assets/social-media-icons/youtube.svg' imgUrl: '/assets/social-media-icons/youtube.svg'
} }
] ]
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {
} }
} }

View File

@ -1,13 +1,13 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { FocusNextInputDirective } from './focus-next-input.directive'; import { FocusNextInputDirective } from './focus-next-input.directive';
import { DownloadAppDirective } from './download-app.directive'; import { DownloadAppDirective } from './download-app.directive';
@NgModule({ @NgModule({
imports: [ imports: [
CommonModule CommonModule
], ],
declarations: [FocusNextInputDirective, DownloadAppDirective], declarations: [FocusNextInputDirective, DownloadAppDirective],
exports: [FocusNextInputDirective, DownloadAppDirective] exports: [FocusNextInputDirective, DownloadAppDirective]
}) })
export class DirectivesModule { } export class DirectivesModule { }

View File

@ -1,81 +1,81 @@
import { import {
Directive, Directive,
ElementRef, ElementRef,
HostListener, HostListener,
OnInit, OnInit,
Renderer2, Renderer2,
} from '@angular/core'; } from '@angular/core';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { getTypeDevice, pwaInstalled } from 'src/app/utils'; import { getTypeDevice, pwaInstalled } from 'src/app/utils';
@Directive({ @Directive({
selector: '[appDownloadApp]', selector: '[appDownloadApp]',
}) })
export class DownloadAppDirective implements OnInit { export class DownloadAppDirective implements OnInit {
public deviceType: 'ios' | 'android' | null = null; public deviceType: 'ios' | 'android' | null = null;
public deferredPrompt: any; public deferredPrompt: any;
constructor( constructor(
private messageService: MessageService, private messageService: MessageService,
public renderer: Renderer2, public renderer: Renderer2,
private el: ElementRef, private el: ElementRef,
) { } ) { }
ngOnInit(): void { ngOnInit(): void {
getTypeDevice(); getTypeDevice();
if (this.deviceType === 'ios') { if (this.deviceType === 'ios') {
this.el.nativeElement.style.display = 'block'; this.el.nativeElement.style.display = 'block';
} }
this.checkInstalled(); this.checkInstalled();
} }
async checkInstalled(): Promise<void> { async checkInstalled(): Promise<void> {
if (window.matchMedia('(display-mode: standalone)').matches if (window.matchMedia('(display-mode: standalone)').matches
|| await pwaInstalled()) { || await pwaInstalled()) {
this.el.nativeElement.style.display = 'none'; this.el.nativeElement.style.display = 'none';
} }
} }
@HostListener('window:beforeinstallprompt', ['$event']) @HostListener('window:beforeinstallprompt', ['$event'])
onBeforeInstallPrompt(e: any) { onBeforeInstallPrompt(e: any) {
e.preventDefault(); e.preventDefault();
this.deferredPrompt = e; this.deferredPrompt = e;
if (this.deferredPrompt) { if (this.deferredPrompt) {
this.el.nativeElement.style.display = 'block'; this.el.nativeElement.style.display = 'block';
} }
} }
@HostListener('window:appinstalled', ['$event']) @HostListener('window:appinstalled', ['$event'])
onAppInstalled(e: any) { onAppInstalled(e: any) {
// Prevent Chrome 67 and earlier from automatically showing the prompt // Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault(); e.preventDefault();
// Stash the event so it can be triggered later. // Stash the event so it can be triggered later.
this.deferredPrompt = e; this.deferredPrompt = e;
this.el.nativeElement.style.display = 'none'; this.el.nativeElement.style.display = 'none';
} }
@HostListener('click', ['$event']) @HostListener('click', ['$event'])
async downloadApp(event: MouseEvent) { async downloadApp(event: MouseEvent) {
if (!this.deferredPrompt) { if (!this.deferredPrompt) {
this.messageService.clear(); this.messageService.clear();
this.messageService.add({ this.messageService.add({
severity: 'error', severity: 'error',
summary: 'Не поддерживается в Вашем браузере!', summary: 'Не поддерживается в Вашем браузере!',
}); });
return; return;
} }
this.deferredPrompt.prompt(); this.deferredPrompt.prompt();
this.deferredPrompt.userChoice.then((res: any) => { this.deferredPrompt.userChoice.then((res: any) => {
if (res.outcome === 'accepted') { if (res.outcome === 'accepted') {
this.messageService.clear(); this.messageService.clear();
this.messageService.add({ this.messageService.add({
severity: 'success', severity: 'success',
summary: 'Спасибо за установку!', summary: 'Спасибо за установку!',
}); });
} }
this.deferredPrompt = null; this.deferredPrompt = null;
}); });
} }
} }

View File

@ -1,29 +1,29 @@
import { Directive, EventEmitter, Input, Renderer2 } from '@angular/core'; import { Directive, EventEmitter, Input, Renderer2 } from '@angular/core';
@Directive({ @Directive({
selector: '[appFocusNextInput]' selector: '[appFocusNextInput]'
}) })
export class FocusNextInputDirective { export class FocusNextInputDirective {
@Input('appFocusNextInput') eventEmitter!: EventEmitter<string>; @Input('appFocusNextInput') eventEmitter!: EventEmitter<string>;
constructor(private renderer: Renderer2) { } constructor(private renderer: Renderer2) { }
ngOnInit() { ngOnInit() {
// TODO: // TODO:
// 1: don't need to listen all events // 1: don't need to listen all events
// 2: not working in safari // 2: not working in safari
this.eventEmitter.subscribe(elementId => { this.eventEmitter.subscribe(elementId => {
try { try {
const element = this.renderer.selectRootElement(elementId) const element = this.renderer.selectRootElement(elementId)
setTimeout(() => { setTimeout(() => {
element.focus(); element.focus();
element.click(); element.click();
}, 0); }, 0);
} catch (ex) { } catch (ex) {
(document.activeElement as HTMLElement).blur(); (document.activeElement as HTMLElement).blur();
// If the element doesn't exist or if the element disappears when this called then no need to do anything // If the element doesn't exist or if the element disappears when this called then no need to do anything
} }
}); });
} }
} }

View File

@ -1,16 +1,16 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { AuthGuard } from './auth-guard.guard'; import { AuthGuard } from './auth-guard.guard';
describe('AuthGuardGuard', () => { describe('AuthGuardGuard', () => {
let guard: AuthGuard; let guard: AuthGuard;
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({}); TestBed.configureTestingModule({});
guard = TestBed.inject(AuthGuard); guard = TestBed.inject(AuthGuard);
}); });
it('should be created', () => { it('should be created', () => {
expect(guard).toBeTruthy(); expect(guard).toBeTruthy();
}); });
}); });

View File

@ -1,47 +1,47 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { import {
ActivatedRouteSnapshot, ActivatedRouteSnapshot,
CanActivate, CanActivate,
CanActivateChild, CanActivateChild,
Router, Router,
RouterStateSnapshot, RouterStateSnapshot,
UrlTree, UrlTree,
} from '@angular/router'; } from '@angular/router';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { CookiesService } from '../services/cookies.service'; import { CookiesService } from '../services/cookies.service';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class AuthGuard implements CanActivate, CanActivateChild { export class AuthGuard implements CanActivate, CanActivateChild {
constructor(private cookiesService: CookiesService, private router: Router) {} constructor(private cookiesService: CookiesService, private router: Router) {}
canActivate( canActivate(
route: ActivatedRouteSnapshot, route: ActivatedRouteSnapshot,
state: RouterStateSnapshot state: RouterStateSnapshot
): ):
| Observable<boolean | UrlTree> | Observable<boolean | UrlTree>
| Promise<boolean | UrlTree> | Promise<boolean | UrlTree>
| boolean | boolean
| UrlTree | UrlTree
{ {
if (this.cookiesService.getItem('token')) { if (this.cookiesService.getItem('token')) {
return of(true); return of(true);
} else { } else {
this.router.navigate(['/login']); this.router.navigate(['/login']);
return of(false); return of(false);
} }
} }
canActivateChild( canActivateChild(
childRoute: ActivatedRouteSnapshot, childRoute: ActivatedRouteSnapshot,
state: RouterStateSnapshot state: RouterStateSnapshot
): ):
| boolean | boolean
| UrlTree | UrlTree
| Observable<boolean | UrlTree> | Observable<boolean | UrlTree>
| Promise<boolean | UrlTree> | Promise<boolean | UrlTree>
{ {
return this.canActivate(childRoute, state); return this.canActivate(childRoute, state);
} }
} }

View File

@ -1,222 +1,222 @@
export enum PageCode { export enum PageCode {
Auth, Auth,
Orders, Orders,
BonusProgram, BonusProgram,
RefSystem, RefSystem,
UserData, UserData,
} }
export interface Moment extends moment.Moment { } export interface Moment extends moment.Moment { }
export interface Page { export interface Page {
code: PageCode; code: PageCode;
component?: any; component?: any;
name: string; name: string;
description?: string; description?: string;
getMethod?: string; getMethod?: string;
resName?: string; resName?: string;
onSideBar: boolean; onSideBar: boolean;
} }
export interface UserDataForm { export interface UserDataForm {
first_name: string; first_name: string;
birthdate: string; birthdate: string;
gender: string; gender: string;
} }
export interface BonusProgramAccount { export interface BonusProgramAccount {
BonusProgramName: string; BonusProgramName: string;
BonusProgramTypeID: string; BonusProgramTypeID: string;
CardNumber: number; CardNumber: number;
Bonuses: number; Bonuses: number;
HoldedBonuses: number; HoldedBonuses: number;
BonusProgramAccounts: BonusProgramAccount[]; BonusProgramAccounts: BonusProgramAccount[];
DateBonusBurn: string; DateBonusBurn: string;
_links: any[]; _links: any[];
_embedded: any; _embedded: any;
} }
export interface Purchase { export interface Purchase {
PurchaseId?: string; PurchaseId?: string;
CustomerId?: string; CustomerId?: string;
PurchaseDate: string; PurchaseDate: string;
PurchaseState?: number; PurchaseState?: number;
CardNumber?: number; CardNumber?: number;
Address?: string; Address?: string;
CheckSummary?: number; CheckSummary?: number;
BonusSummary?: number; BonusSummary?: number;
ID: string; ID: string;
Transactions: Transaction[]; Transactions: Transaction[];
IsSingleTransaction?: boolean; IsSingleTransaction?: boolean;
transactionCreateDate?: string; transactionCreateDate?: string;
transactionType?: 'CancelPayFromWallet' | 'PayFromWallet' | 'RefillWallet'; transactionType?: 'CancelPayFromWallet' | 'PayFromWallet' | 'RefillWallet';
transactionSum: number; transactionSum: number;
orderSum?: number; orderSum?: number;
} }
export interface Transaction { export interface Transaction {
User: string; User: string;
Purchase: string; Purchase: string;
Date: string; Date: string;
Value: number; Value: number;
TransactionType: number; TransactionType: number;
UserBonusesSnapshot: number; UserBonusesSnapshot: number;
BonusPercent: number; BonusPercent: number;
DateActiveBonus: string; DateActiveBonus: string;
AccountBonus: string; AccountBonus: string;
Bonus: string; Bonus: string;
ID: string; ID: string;
HasPurchase?: boolean; HasPurchase?: boolean;
} }
export interface OrderStatus { export interface OrderStatus {
[key: string]: string; [key: string]: string;
} }
export interface lvlPeriod { export interface lvlPeriod {
percent: number; percent: number;
start: number; start: number;
end?: number; end?: number;
color: string; color: string;
} }
export interface DeliveryType { export interface DeliveryType {
cost: number; cost: number;
title: string; title: string;
id: number; id: number;
} }
export interface AcceptedOrder { export interface AcceptedOrder {
id: number; id: number;
status: string; status: string;
currency_symbol: string; currency_symbol: string;
total: number; total: number;
address: { address: {
city: string; city: string;
street: string; street: string;
house: number; house: number;
flat: number; flat: number;
}; };
payment_method: string; payment_method: string;
shipping: { shipping: {
name: string; name: string;
total: number; total: number;
}; };
date_created: string; date_created: string;
items: OrderProduct[]; items: OrderProduct[];
} }
export interface Product { export interface Product {
id: number; id: number;
name: string; name: string;
price: string; price: string;
image_url: string; image_url: string;
image_gallery: string[]; image_gallery: string[];
category_id: number; category_id: number;
description?: string; description?: string;
stock_status: string; stock_status: string;
currency_symbol: string; currency_symbol: string;
modifier_data: Modifier[]; modifier_data: Modifier[];
short_description?: string; short_description?: string;
guid?: string; guid?: string;
} }
export interface Modifier { export interface Modifier {
id: number; id: number;
name: string; name: string;
category_type: string; category_type: string;
minimum_options: number; minimum_options: number;
maximum_options: number; maximum_options: number;
global_categories: string; global_categories: string;
required: number; required: number;
options: Option[]; options: Option[];
allOptions?: Option[]; allOptions?: Option[];
} }
export interface Option { export interface Option {
id: number; id: number;
name: string; name: string;
price: string; price: string;
prechecked: string; prechecked: string;
active?: boolean; active?: boolean;
} }
export interface OrderProduct { export interface OrderProduct {
id: number; id: number;
amount: number; amount: number;
name: string; name: string;
price: number; price: number;
modifiers: { modifiers: {
[name: string]: OrderModifier[]; [name: string]: OrderModifier[];
}; };
} }
export interface OrderModifier { export interface OrderModifier {
name: string; name: string;
id: number; id: number;
price: number; price: number;
} }
export interface DeliveryData { export interface DeliveryData {
paymentMethod: PaymentMethod; paymentMethod: PaymentMethod;
deliveryDate: Date | null; deliveryDate: Date | null;
deliveryType: DeliveryType | null; deliveryType: DeliveryType | null;
persons: number; persons: number;
comment: string; comment: string;
} }
export interface PaymentMethod { export interface PaymentMethod {
type: string; type: string;
label: string; label: string;
} }
export interface UserData { export interface UserData {
first_name: string | null; first_name: string | null;
last_name: string | null; last_name: string | null;
street: string | null; street: string | null;
house: string | null; house: string | null;
flat: string | null; flat: string | null;
city: string; city: string;
phone: string | null; phone: string | null;
} }
export interface UserInfoCategory { export interface UserInfoCategory {
id: string; id: string;
isActive: boolean; isActive: boolean;
isDefaultForNewGuests: boolean; isDefaultForNewGuests: boolean;
name: string; name: string;
} }
export interface UserInfoWallet { export interface UserInfoWallet {
id: string; id: string;
name: string; name: string;
programType: string; programType: string;
type: string; type: string;
} }
export interface UserInfoWalletBalance { export interface UserInfoWalletBalance {
balance: number; balance: number;
wallet: UserInfoWallet; wallet: UserInfoWallet;
} }
export interface UserInfo { export interface UserInfo {
OrdersSum: number; OrdersSum: number;
categories: UserInfoCategory[]; categories: UserInfoCategory[];
customer_level: number; customer_level: number;
birthday: string; birthday: string;
id: string; id: string;
name: string | null; name: string | null;
phone: string; phone: string;
walletBalances: UserInfoWalletBalance[] | { error: ResponseError }; walletBalances: UserInfoWalletBalance[] | { error: ResponseError };
} }
export interface ResponseError { export interface ResponseError {
code: number; code: number;
msg: string; msg: string;
} }
export interface UpdateNewCustomerRequest { export interface UpdateNewCustomerRequest {
name: string; name: string;
sex: string; sex: string;
birthday: string; birthday: string;
} }

View File

@ -1,62 +1,62 @@
import {Modifier, Product} from "../interface/data"; import {Modifier, Product} from "../interface/data";
export class OrderProduct implements Product{ export class OrderProduct implements Product{
constructor(product: Product,guid: string, amount: number = 1) { constructor(product: Product,guid: string, amount: number = 1) {
this.category_id = product.category_id; this.category_id = product.category_id;
this.currency_symbol = product.currency_symbol; this.currency_symbol = product.currency_symbol;
this.description = product.description; this.description = product.description;
this.id = product.id; this.id = product.id;
this.image_gallery = product.image_gallery; this.image_gallery = product.image_gallery;
this.image_url = product.image_url; this.image_url = product.image_url;
this.modifier_data = product.modifier_data; this.modifier_data = product.modifier_data;
this.name = product.name; this.name = product.name;
this.price = product.price; this.price = product.price;
this.stock_status = product.stock_status; this.stock_status = product.stock_status;
this.amount = amount; this.amount = amount;
this.guid = guid; this.guid = guid;
this.short_description = product.short_description; this.short_description = product.short_description;
} }
public amount: number; public amount: number;
public category_id: number; public category_id: number;
public currency_symbol: string; public currency_symbol: string;
public description?: string; public description?: string;
public short_description?: string; public short_description?: string;
public id: number; public id: number;
public image_gallery: string[]; public image_gallery: string[];
public image_url: string; public image_url: string;
public modifier_data: Modifier[]; public modifier_data: Modifier[];
public name: string; public name: string;
public price: string; public price: string;
public stock_status: string; public stock_status: string;
public guid: string; public guid: string;
get finalPrice(): number{ get finalPrice(): number{
const modifiersPrice = this.modifier_data.reduce<number>((previousValue, currentValue) => { const modifiersPrice = this.modifier_data.reduce<number>((previousValue, currentValue) => {
return previousValue + currentValue.options.reduce<number>((previousOptionValue, currentOptionValue) => { return previousValue + currentValue.options.reduce<number>((previousOptionValue, currentOptionValue) => {
return previousOptionValue + Number(currentOptionValue.price); return previousOptionValue + Number(currentOptionValue.price);
}, 0); }, 0);
}, 0); }, 0);
return (Number(this.price) + modifiersPrice) * this.amount; return (Number(this.price) + modifiersPrice) * this.amount;
new Date() new Date()
} }
toJson(){ toJson(){
return { return {
id: this.id, id: this.id,
amount: this.amount, amount: this.amount,
name: this.name, name: this.name,
modifiers: this.modifier_data?.map(modifier => { modifiers: this.modifier_data?.map(modifier => {
return { return {
id: modifier.id, id: modifier.id,
options: modifier.options, options: modifier.options,
} }
}), }),
} }
} }
} }

View File

@ -1,68 +1,68 @@
import {DeliveryData, UserData} from "../interface/data"; import {DeliveryData, UserData} from "../interface/data";
import {OrderProduct} from "./order-product"; import {OrderProduct} from "./order-product";
import * as moment from 'moment'; import * as moment from 'moment';
import { CookiesService } from "../services/cookies.service"; import { CookiesService } from "../services/cookies.service";
export interface OrderInfo { export interface OrderInfo {
products: OrderProduct[]; products: OrderProduct[];
userData?: UserData; userData?: UserData;
deliveryData?: DeliveryData; deliveryData?: DeliveryData;
phone: string; phone: string;
token: string | undefined; token: string | undefined;
} }
export class Order { export class Order {
public products: OrderProduct[]; public products: OrderProduct[];
public userData?: UserData; public userData?: UserData;
public deliveryData?: DeliveryData; public deliveryData?: DeliveryData;
public phone: string; public phone: string;
public token: string | undefined; public token: string | undefined;
constructor( constructor(
orderInfo: OrderInfo orderInfo: OrderInfo
) { ) {
this.products = orderInfo.products; this.products = orderInfo.products;
this.userData = orderInfo.userData; this.userData = orderInfo.userData;
this.deliveryData = orderInfo.deliveryData; this.deliveryData = orderInfo.deliveryData;
this.phone = orderInfo.phone; this.phone = orderInfo.phone;
this.token = orderInfo.token; this.token = orderInfo.token;
} }
get price(): number { get price(): number {
return this.products.reduce<number>((previousValue, currentValue) => previousValue + currentValue.finalPrice, 0); return this.products.reduce<number>((previousValue, currentValue) => previousValue + currentValue.finalPrice, 0);
} }
toJson(): any { toJson(): any {
const date = moment(this.deliveryData?.deliveryDate ?? Date.now()); const date = moment(this.deliveryData?.deliveryDate ?? Date.now());
return { return {
items: this.products.map(product => { items: this.products.map(product => {
return product.toJson(); return product.toJson();
}), }),
user_data: { user_data: {
phone: this.phone, phone: this.phone,
...this.userData ...this.userData
}, },
payment_method: this.deliveryData?.paymentMethod.type, payment_method: this.deliveryData?.paymentMethod.type,
delivery_time: date.format('HH:mm'), delivery_time: date.format('HH:mm'),
delivery_date: date.format("YYYY-MM-DD"), delivery_date: date.format("YYYY-MM-DD"),
delivery_instance_id: this.deliveryData?.deliveryType?.id, delivery_instance_id: this.deliveryData?.deliveryType?.id,
persons: 1, persons: 1,
payments: [ payments: [
{ {
type: this.deliveryData?.paymentMethod.type, type: this.deliveryData?.paymentMethod.type,
summ: this.price, summ: this.price,
}, },
{ {
type: "crm4retail", type: "crm4retail",
summ: 0, summ: 0,
payload: { payload: {
id: "c07a10d8-ba7e-43b0-92aa-ae470060bc7d" id: "c07a10d8-ba7e-43b0-92aa-ae470060bc7d"
} }
} }
], ],
comment: this.deliveryData?.comment, comment: this.deliveryData?.comment,
token: this.token token: this.token
} }
} }
} }

View File

@ -1,96 +1,96 @@
<div <div
[ngClass]="{ [ngClass]="{
woocommerce: true, woocommerce: true,
'auth-page': currentPage.code === PageCode.Auth 'auth-page': currentPage.code === PageCode.Auth
}" }"
> >
<!-- <div <!-- <div
*ngIf="currentPage.code !== PageCode.Auth" *ngIf="currentPage.code !== PageCode.Auth"
class="top-left-attribute" class="top-left-attribute"
></div> ></div>
<div [ngSwitch]="currentPage.code" class=""> <div [ngSwitch]="currentPage.code" class="">
<ng-container *ngSwitchCase="PageCode.Auth"> <ng-container *ngSwitchCase="PageCode.Auth">
<app-auth <app-auth
[handleHttpError]="handleHttpErrorFunc" [handleHttpError]="handleHttpErrorFunc"
(phoneConfirmed)="phoneConfirmed()" (phoneConfirmed)="phoneConfirmed()"
></app-auth> ></app-auth>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="PageCode.Orders"> <ng-container *ngSwitchCase="PageCode.Orders">
<app-orders></app-orders> <app-orders></app-orders>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="PageCode.BonusProgram"> <ng-container *ngSwitchCase="PageCode.BonusProgram">
<app-bonus-program></app-bonus-program> <app-bonus-program></app-bonus-program>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="PageCode.UserData"> <ng-container *ngSwitchCase="PageCode.UserData">
<app-user-data></app-user-data> <app-user-data></app-user-data>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="PageCode.RefSystem"> <ng-container *ngSwitchCase="PageCode.RefSystem">
<app-ref-system></app-ref-system> <app-ref-system></app-ref-system>
</ng-container> </ng-container>
</div> </div>
<nav <nav
*ngIf="currentPage.code !== PageCode.Auth" *ngIf="currentPage.code !== PageCode.Auth"
class="woocommerce-MyAccount-navigation" class="woocommerce-MyAccount-navigation"
> >
<ul> <ul>
<ng-container *ngFor="let page of pageList; let index = index"> <ng-container *ngFor="let page of pageList; let index = index">
<li <li
*ngIf="page.onSideBar" *ngIf="page.onSideBar"
class="woocommerce-MyAccount-navigation-link" class="woocommerce-MyAccount-navigation-link"
[ngClass]="{ [ngClass]="{
'is-active': page === currentPage, 'is-active': page === currentPage,
first: index === 1 first: index === 1
}" }"
(click)="changePage($event, page)" (click)="changePage($event, page)"
> >
<div class="container"> <div class="container">
<img <img
style="width: 36px" style="width: 36px"
src="{{ './assets/menu-icons/' + page.resName + '.png' }}" src="{{ './assets/menu-icons/' + page.resName + '.png' }}"
alt="Иконка меню" alt="Иконка меню"
/> />
<div class="menu-item-info"> <div class="menu-item-info">
<a href="#">{{ page.name }}</a> <a href="#">{{ page.name }}</a>
<p>{{ page.description }}</p> <p>{{ page.description }}</p>
</div> </div>
</div> </div>
</li> </li>
</ng-container> </ng-container>
<li <li
class="woocommerce-MyAccount-navigation-link download-app" class="woocommerce-MyAccount-navigation-link download-app"
appDownloadApp appDownloadApp
> >
<div class="container"> <div class="container">
<img <img
style="width: 36px" style="width: 36px"
src="./assets/menu-icons/download-app.png" src="./assets/menu-icons/download-app.png"
alt="Иконка меню" alt="Иконка меню"
/> />
<div class="menu-item-info"> <div class="menu-item-info">
<a href="#">Установить приложение</a> <a href="#">Установить приложение</a>
<p>{{ page.description }}</p> <p>{{ page.description }}</p>
</div> </div>
</div> </div>
</li> </li>
<li <li
class="woocommerce-MyAccount-navigation-link" class="woocommerce-MyAccount-navigation-link"
(click)="logout($event)" (click)="logout($event)"
> >
<div class="container"> <div class="container">
<img src="./assets/menu-icons/exit.png" alt="Иконка меню" /> <img src="./assets/menu-icons/exit.png" alt="Иконка меню" />
<div class="menu-item-info"> <div class="menu-item-info">
<a href="#">Выход</a> <a href="#">Выход</a>
</div> </div>
</div> </div>
</li> </li>
</ul> </ul>
</nav> --> </nav> -->
<span <span
class="version" class="version"
[ngClass]="{ [ngClass]="{
version: true version: true
}" }"
> >
v{{ version }} v{{ version }}
</span> </span>
</div> </div>

View File

@ -1,100 +1,100 @@
:host { :host {
.woocommerce { .woocommerce {
min-height: calc(100vh - 39px); min-height: calc(100vh - 39px);
padding: 20px 18px; padding: 20px 18px;
&.auth-page { &.auth-page {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
} }
nav { nav {
margin-bottom: 24px; margin-bottom: 24px;
margin-top: 26px; margin-top: 26px;
display: flex; display: flex;
justify-content: center; justify-content: center;
ul { ul {
max-width: 400px; max-width: 400px;
width: 100%; width: 100%;
border-radius: 6px; border-radius: 6px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
flex-direction: column; flex-direction: column;
li { li {
padding: 12px; padding: 12px;
width: 100%; width: 100%;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
height: 81px; height: 81px;
margin-bottom: 10px; margin-bottom: 10px;
background: #ffffff; background: #ffffff;
box-shadow: 0px 0px 5px rgb(0 0 0 / 15%); box-shadow: 0px 0px 5px rgb(0 0 0 / 15%);
color: #000; color: #000;
border-radius: 15px; border-radius: 15px;
&.is-active { &.is-active {
border: solid var(--main-color) 1px; border: solid var(--main-color) 1px;
// display: none; // display: none;
} }
&.first { &.first {
// border-radius: 7px 0 0 7px; // border-radius: 7px 0 0 7px;
} }
&.download-app { &.download-app {
display: none; display: none;
} }
.container { .container {
display: flex; display: flex;
align-items: center; align-items: center;
height: 100%; height: 100%;
.menu-item-info { .menu-item-info {
margin-left: 16px; margin-left: 16px;
&>a { &>a {
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 18px; font-size: 18px;
line-height: 22px; line-height: 22px;
text-decoration: none; text-decoration: none;
color: #000; color: #000;
display: block; display: block;
text-align-last: left; text-align-last: left;
} }
&>p { &>p {
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 15px; line-height: 15px;
text-align: left; text-align: left;
} }
} }
} }
} }
li:nth-child(odd) { li:nth-child(odd) {
background-color: #ebebeb; background-color: #ebebeb;
} }
} }
} }
.top-left-attribute { .top-left-attribute {
width: 10px; width: 10px;
height: 5px; height: 5px;
background: var(--main-color); background: var(--main-color);
left: 0; left: 0;
position: absolute; position: absolute;
top: 103px; top: 103px;
} }
.version { .version {
opacity: 0.5; opacity: 0.5;
} }
} }
} }

View File

@ -1,205 +1,205 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { CookiesService } from '../../services/cookies.service'; import { CookiesService } from '../../services/cookies.service';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Page, PageCode } from '../../interface/data'; import { Page, PageCode } from '../../interface/data';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
import { PageList, PageListWithBonus } from '../../app.constants'; import { PageList, PageListWithBonus } from '../../app.constants';
import { HttpErrorResponse, HttpParams } from '@angular/common/http'; import { HttpErrorResponse, HttpParams } from '@angular/common/http';
import { ExitComponent } from '../../components/exit/exit.component'; import { ExitComponent } from '../../components/exit/exit.component';
import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog'; import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { lastValueFrom } from 'rxjs'; import { lastValueFrom } from 'rxjs';
@Component({ @Component({
selector: 'app-account', selector: 'app-account',
templateUrl: './account.component.html', templateUrl: './account.component.html',
styleUrls: ['./account.component.scss'], styleUrls: ['./account.component.scss'],
providers: [DialogService], providers: [DialogService],
}) })
export class AccountComponent implements OnInit { export class AccountComponent implements OnInit {
@Output() setUserDataOrderPage = new EventEmitter<null>(); @Output() setUserDataOrderPage = new EventEmitter<null>();
public refSystemId: string = ''; public refSystemId: string = '';
constructor( constructor(
private cookiesService: CookiesService, private cookiesService: CookiesService,
private router: Router, private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,
private dialogService: DialogService, private dialogService: DialogService,
private jsonRpcService: JsonrpcService, private jsonRpcService: JsonrpcService,
private messageService: MessageService private messageService: MessageService
) {} ) {}
public currentPage!: Page; public currentPage!: Page;
public handleHttpErrorFunc = this.handleHttpError.bind(this); public handleHttpErrorFunc = this.handleHttpError.bind(this);
private ref!: DynamicDialogRef; private ref!: DynamicDialogRef;
public version: string = environment.version; public version: string = environment.version;
readonly PageCode = PageCode; readonly PageCode = PageCode;
readonly pageList = environment.hasBonusProgram readonly pageList = environment.hasBonusProgram
? PageListWithBonus ? PageListWithBonus
: PageList; : PageList;
ngOnInit(): void { ngOnInit(): void {
if (!this.getToken()) { if (!this.getToken()) {
this.currentPage = this.pageList[0]; this.currentPage = this.pageList[0];
} else { } else {
this.route.queryParams.subscribe((params) => { this.route.queryParams.subscribe((params) => {
if (!params['activePage']) { if (!params['activePage']) {
this.currentPage = this.pageList[1]; this.currentPage = this.pageList[1];
return; return;
} }
const currentPage = this.pageList.find((page) => { const currentPage = this.pageList.find((page) => {
return page.code === Number(params['activePage']); return page.code === Number(params['activePage']);
}); });
if (!currentPage) { if (!currentPage) {
this.currentPage = this.pageList[1]; this.currentPage = this.pageList[1];
} else { } else {
this.currentPage = currentPage; this.currentPage = currentPage;
} }
}); });
} }
document.body.classList.add( document.body.classList.add(
'woocommerce-account', 'woocommerce-account',
'woocommerce-page', 'woocommerce-page',
'woocommerce-orders' 'woocommerce-orders'
); );
} }
phoneConfirmed(): void { phoneConfirmed(): void {
this.refSystem(); this.refSystem();
this.currentPage = this.pageList[1]; this.currentPage = this.pageList[1];
} }
async refSystem() { async refSystem() {
const additionalInfo = (await lastValueFrom( const additionalInfo = (await lastValueFrom(
this.jsonRpcService.rpc({ this.jsonRpcService.rpc({
method: 'getAdditionalInfo', method: 'getAdditionalInfo',
params: [] params: []
}, RpcService.authService, true) }, RpcService.authService, true)
)).data )).data
this.route.queryParams.subscribe((params) => { this.route.queryParams.subscribe((params) => {
if (params['refUserId']) { if (params['refUserId']) {
if (additionalInfo.refSystem?.code.length) { if (additionalInfo.refSystem?.code.length) {
this.messageService.add({ this.messageService.add({
severity: 'custom', severity: 'custom',
summary: summary:
'Вы уже зарегестрированы в реферальной программе!', 'Вы уже зарегестрированы в реферальной программе!',
}); });
return; return;
} }
this.refSystemId = params['refUserId']; this.refSystemId = params['refUserId'];
this.jsonRpcService this.jsonRpcService
.rpc( .rpc(
{ {
method: 'updateAdditionalInfo', method: 'updateAdditionalInfo',
params: [ params: [
{ {
refSystem: { refSystem: {
code: this.refSystemId, code: this.refSystemId,
}, },
}, },
], ],
}, },
RpcService.authService, RpcService.authService,
true true
) )
.subscribe({ .subscribe({
next: () => { next: () => {
this.router.navigate([], { this.router.navigate([], {
queryParams: { queryParams: {
refUserId: null, refUserId: null,
}, },
queryParamsHandling: 'merge', queryParamsHandling: 'merge',
}); });
this.messageService.add({ this.messageService.add({
severity: 'custom', severity: 'custom',
summary: summary:
'Регистрация прошла успешна! Бонусы скоро будут начислены', 'Регистрация прошла успешна! Бонусы скоро будут начислены',
}); });
}, },
error: (err) => { error: (err) => {
console.error('Error: ', err); console.error('Error: ', err);
this.messageService.add({ this.messageService.add({
severity: 'error', severity: 'error',
summary: 'Произошла ошибка, попробуйте позже', summary: 'Произошла ошибка, попробуйте позже',
}); });
}, },
}); });
} }
}); });
} }
changePage(event: MouseEvent, page: Page): void { changePage(event: MouseEvent, page: Page): void {
event.preventDefault(); event.preventDefault();
if (page.resName === 'download-app') { if (page.resName === 'download-app') {
return return
} }
this.currentPage = page; this.currentPage = page;
// let params = new HttpParams(); // let params = new HttpParams();
// params = params.append('activePage', this.currentPage.code); // params = params.append('activePage', this.currentPage.code);
this.router.navigate(['.'], { this.router.navigate(['.'], {
relativeTo: this.route, relativeTo: this.route,
queryParams: { queryParams: {
activePage: this.currentPage.code, activePage: this.currentPage.code,
}, },
queryParamsHandling: 'merge', queryParamsHandling: 'merge',
// preserve the existing query params in the route // preserve the existing query params in the route
// skipLocationChange: true // skipLocationChange: true
// do not trigger navigation // do not trigger navigation
}); });
} }
handleHttpError(error: HttpErrorResponse): void { handleHttpError(error: HttpErrorResponse): void {
if (error.status === 500) { if (error.status === 500) {
this.logout(); this.logout();
} }
} }
setToken(token: string): void { setToken(token: string): void {
this.cookiesService.setCookie('token', token); this.cookiesService.setCookie('token', token);
} }
getToken(): string | void { getToken(): string | void {
return this.cookiesService.getItem('token'); return this.cookiesService.getItem('token');
} }
deleteToken(): void { deleteToken(): void {
this.cookiesService.logout(); this.cookiesService.logout();
// this.router.navigate(['.'], { // this.router.navigate(['.'], {
// queryParams: {}, // queryParams: {},
// }); // });
} }
logout(event?: MouseEvent) { logout(event?: MouseEvent) {
if (event) { if (event) {
event.preventDefault(); event.preventDefault();
} }
this.ref = this.dialogService.open(ExitComponent, { this.ref = this.dialogService.open(ExitComponent, {
width: 'auto', width: 'auto',
style: { style: {
'max-width': '90vw', 'max-width': '90vw',
'max-height': '90vh', 'max-height': '90vh',
}, },
contentStyle: { contentStyle: {
'max-height': '90vh', 'max-height': '90vh',
height: 'auto', height: 'auto',
'max-width': '90vw', 'max-width': '90vw',
overflow: 'auto', overflow: 'auto',
'border-radius': '4px', 'border-radius': '4px',
}, },
baseZIndex: 10000, baseZIndex: 10000,
autoZIndex: true, autoZIndex: true,
dismissableMask: true, dismissableMask: true,
closeOnEscape: true, closeOnEscape: true,
showHeader: false, showHeader: false,
}); });
this.ref.onClose.subscribe((result) => { this.ref.onClose.subscribe((result) => {
if (result) { if (result) {
this.deleteToken(); this.deleteToken();
this.currentPage = this.pageList[0]; this.currentPage = this.pageList[0];
} }
}); });
} }
} }

View File

@ -1,98 +1,98 @@
<div class="woocommerce-notices-wrapper"></div> <div class="woocommerce-notices-wrapper"></div>
<ng-container *ngIf="!isCodeConfirm; else confirmPhoneField"> <ng-container *ngIf="!isCodeConfirm; else confirmPhoneField">
<form <form
class="woocommerce-form woocommerce-form-login login" class="woocommerce-form woocommerce-form-login login"
(submit)="getCode($event)" (submit)="getCode($event)"
> >
<h2>Вход</h2> <h2>Вход</h2>
<p <p
class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide" class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide"
> >
<label for="phone">Введите Ваш номер телефона</label> <label for="phone">Введите Ваш номер телефона</label>
<p-inputMask <p-inputMask
mask="+7 (999) 999-99-99" mask="+7 (999) 999-99-99"
characterPattern="[0-9]" characterPattern="[0-9]"
styleClass="woocommerce-Input woocommerce-Input--text input-text" styleClass="woocommerce-Input woocommerce-Input--text input-text"
name="phone" name="phone"
type="tel" type="tel"
inputId="phone" inputId="phone"
autocomplete="phone" autocomplete="phone"
placeholder="+7 (000) 000-00-00" placeholder="+7 (000) 000-00-00"
mask="+7 (999) 999-9999" mask="+7 (999) 999-9999"
[unmask]="false" [unmask]="false"
[(ngModel)]="phone" [(ngModel)]="phone"
></p-inputMask> ></p-inputMask>
</p> </p>
<p class="form-row"> <p class="form-row">
<button <button
type="submit" type="submit"
class="woocommerce-button button woocommerce-form-login__submit" class="woocommerce-button button woocommerce-form-login__submit"
name="login" name="login"
value="Получить код" value="Получить код"
[disabled]="loading || phone.length < 10" [disabled]="loading || phone.length < 10"
> >
{{ loading ? "" : "Получить код" }} {{ loading ? "" : "Получить код" }}
<p-progressSpinner <p-progressSpinner
*ngIf="loading" *ngIf="loading"
[style]="{ width: '16px', height: '16px' }" [style]="{ width: '16px', height: '16px' }"
strokeWidth="2" strokeWidth="2"
styleClass="angular-spinner" styleClass="angular-spinner"
></p-progressSpinner> ></p-progressSpinner>
</button> </button>
</p> </p>
<div class="decoration-pattern"></div> <div class="decoration-pattern"></div>
</form> </form>
</ng-container> </ng-container>
<ng-template #confirmPhoneField> <ng-template #confirmPhoneField>
<form <form
class="woocommerce-form woocommerce-form-login login" class="woocommerce-form woocommerce-form-login login"
action="false" action="false"
(submit)="confirmCode($event)" (submit)="confirmCode($event)"
> >
<h2>Вход</h2> <h2>Вход</h2>
<p <p
class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide" class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide"
> >
<label for="code"> <label for="code">
Введите 4 цифры из смс, которое пришло на Ваш номер телефона Введите 4 цифры из смс, которое пришло на Ваш номер телефона
</label> </label>
<input <input
pInputText pInputText
type="number" type="number"
pattern="\d*" pattern="\d*"
class="woocommerce-Input woocommerce-Input--text input-text" class="woocommerce-Input woocommerce-Input--text input-text"
name="code" name="code"
id="code" id="code"
autocomplete="code" autocomplete="code"
placeholder="00-00" placeholder="00-00"
[(ngModel)]="code" [(ngModel)]="code"
/> />
</p> </p>
<p class="form-row" style="margin-bottom: 6px;"> <p class="form-row" style="margin-bottom: 6px;">
<button <button
type="submit" type="submit"
class="woocommerce-button button woocommerce-form-login__submit" class="woocommerce-button button woocommerce-form-login__submit"
name="login" name="login"
value="Войти" value="Войти"
[disabled]="loading" [disabled]="loading"
> >
{{ loading ? "" : "Войти" }} {{ loading ? "" : "Войти" }}
<p-progressSpinner <p-progressSpinner
*ngIf="loading" *ngIf="loading"
[style]="{ width: '16px', height: '16px' }" [style]="{ width: '16px', height: '16px' }"
strokeWidth="2" strokeWidth="2"
styleClass="angular-spinner" styleClass="angular-spinner"
></p-progressSpinner> ></p-progressSpinner>
</button> </button>
</p> </p>
<p class="form-row" style="margin-top: 6px"> <p class="form-row" style="margin-top: 6px">
Не пришел код? Не пришел код?
<span class="resend-code" (click)="getCode($event)">Отправить повторно </span> <span class="resend-code" (click)="getCode($event)">Отправить повторно </span>
<span *ngIf="timeLeft">через {{timeLeft}}с</span> <span *ngIf="timeLeft">через {{timeLeft}}с</span>
</p> </p>
<p style="color: red; margin: 0" *ngIf="errorConfirmCode"> <p style="color: red; margin: 0" *ngIf="errorConfirmCode">
Пароль введен неверно Пароль введен неверно
</p> </p>
<div class="decoration-pattern"></div> <div class="decoration-pattern"></div>
</form> </form>
</ng-template> </ng-template>

View File

@ -1,109 +1,109 @@
:host { :host {
display: flex; display: flex;
justify-content: center; justify-content: center;
.woocommerce-form-login__submit { .woocommerce-form-login__submit {
width: 150px; width: 150px;
} }
h2 { h2 {
color: #fff; color: #fff;
width: 100%; width: 100%;
height: 49px; height: 49px;
text-align: left; text-align: left;
padding-top: 13px; padding-top: 13px;
padding-left: 32px; padding-left: 32px;
background: #0d457e; background: #0d457e;
position: relative; position: relative;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 20px; font-size: 20px;
line-height: 24px; line-height: 24px;
&::before { &::before {
content: ""; content: "";
width: 19px; width: 19px;
height: 4px; height: 4px;
background: #fff; background: #fff;
display: block; display: block;
position: absolute; position: absolute;
left: 0; left: 0;
top: 24px; top: 24px;
} }
} }
.woocommerce-form { .woocommerce-form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
background: #ffffff; background: #ffffff;
box-shadow: 0px 0px 10px rgb(0 0 0 / 15%); box-shadow: 0px 0px 10px rgb(0 0 0 / 15%);
border-radius: 15px; border-radius: 15px;
min-height: 262px; min-height: 262px;
max-width: 400px; max-width: 400px;
overflow: auto; overflow: auto;
position: relative; position: relative;
padding-bottom: 10px; padding-bottom: 10px;
} }
.form-row { .form-row {
width: 80%; width: 80%;
margin-bottom: 16px; margin-bottom: 16px;
max-width: 400px; max-width: 400px;
.button { .button {
width: 100%; width: 100%;
font-weight: 600; font-weight: 600;
letter-spacing: 2px; letter-spacing: 2px;
border: none; border: none;
height: 45px; height: 45px;
background: #b8deff; background: #b8deff;
border-radius: 15px; border-radius: 15px;
color: #000; color: #000;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 16px; font-size: 16px;
line-height: 20px; line-height: 20px;
} }
label { label {
display: inline-block; display: inline-block;
margin-bottom: 8px; margin-bottom: 8px;
margin-top: 15px; margin-top: 15px;
color: #000; color: #000;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 18px; font-size: 18px;
line-height: 22px; line-height: 22px;
} }
.input-text { .input-text {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol"; "Segoe UI Symbol";
font-size: 1rem; font-size: 1rem;
color: #495057; color: #495057;
background: #ffffff; background: #ffffff;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
border: 1px solid #ced4da; border: 1px solid #ced4da;
transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; appearance: none;
border-radius: 4px; border-radius: 4px;
width: 100%; width: 100%;
} }
.resend-code { .resend-code {
color: #0d457e; color: #0d457e;
font-weight: 600; font-weight: 600;
} }
} }
.decoration-pattern { .decoration-pattern {
width: calc(100% - 24px); width: calc(100% - 24px);
height: 34px; height: 34px;
background-image: url('../../../../assets/card-decorative-pattern.png'); background-image: url('../../../../assets/card-decorative-pattern.png');
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: contain; background-size: contain;
} }
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AuthComponent } from './auth.component'; import { AuthComponent } from './auth.component';
describe('AuthComponent', () => { describe('AuthComponent', () => {
let component: AuthComponent; let component: AuthComponent;
let fixture: ComponentFixture<AuthComponent>; let fixture: ComponentFixture<AuthComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ AuthComponent ] declarations: [ AuthComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(AuthComponent); fixture = TestBed.createComponent(AuthComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,101 +1,101 @@
import { HttpErrorResponse } from '@angular/common/http'; import { HttpErrorResponse } from '@angular/common/http';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { CookiesService } from 'src/app/services/cookies.service'; import { CookiesService } from 'src/app/services/cookies.service';
import { FormatPhoneService } from 'src/app/services/format-phone.service'; import { FormatPhoneService } from 'src/app/services/format-phone.service';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
@Component({ @Component({
selector: 'app-auth', selector: 'app-auth',
templateUrl: './auth.component.html', templateUrl: './auth.component.html',
styleUrls: ['./auth.component.scss'] styleUrls: ['./auth.component.scss']
}) })
export class AuthComponent { export class AuthComponent {
@Input() handleHttpError!: (error: HttpErrorResponse) => void; @Input() handleHttpError!: (error: HttpErrorResponse) => void;
@Output() phoneConfirmed = new EventEmitter<null>(); @Output() phoneConfirmed = new EventEmitter<null>();
public phone: string = ''; public phone: string = '';
public isCodeConfirm = false; public isCodeConfirm = false;
public code: string = ''; public code: string = '';
public phoneToConfirm!: string; public phoneToConfirm!: string;
public loading = false; public loading = false;
public errorConfirmCode: boolean = false; public errorConfirmCode: boolean = false;
timeLeft: number = 0; timeLeft: number = 0;
constructor( constructor(
private phoneFormatter: FormatPhoneService, private phoneFormatter: FormatPhoneService,
private jsonrpc: JsonrpcService, private jsonrpc: JsonrpcService,
private cookiesService: CookiesService, private cookiesService: CookiesService,
private router: Router, private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,
private messageService: MessageService, private messageService: MessageService,
) { ) {
} }
getCode(event: any): void { getCode(event: any): void {
event.preventDefault(); event.preventDefault();
this.loading = true; this.loading = true;
this.phoneToConfirm = '+' + this.phone.replace(/\D/g, ''); this.phoneToConfirm = '+' + this.phone.replace(/\D/g, '');
if (this.timeLeft) { if (this.timeLeft) {
this.messageService.add({ this.messageService.add({
severity: 'custom', severity: 'custom',
summary: `Отправить повторно можно через ${this.timeLeft}с`, summary: `Отправить повторно можно через ${this.timeLeft}с`,
}); });
this.loading = false; this.loading = false;
return; return;
} }
this.jsonrpc.rpc({ this.jsonrpc.rpc({
method: 'sendVerifyByPhone', method: 'sendVerifyByPhone',
params: [this.phoneToConfirm] params: [this.phoneToConfirm]
}, RpcService.authService, false).subscribe({ }, RpcService.authService, false).subscribe({
next: (result) => { next: (result) => {
if (result.code === -1) { if (result.code === -1) {
this.messageService.add({ this.messageService.add({
severity: 'error', severity: 'error',
summary: 'Произошла ошибка, попробуйте позже!', summary: 'Произошла ошибка, попробуйте позже!',
}); });
} }
if (result.code === 0) { if (result.code === 0) {
this.isCodeConfirm = true; this.isCodeConfirm = true;
this.timeLeft = 60; this.timeLeft = 60;
const interval = setInterval(() => { const interval = setInterval(() => {
if(this.timeLeft > 0) { if(this.timeLeft > 0) {
this.timeLeft--; this.timeLeft--;
} else { } else {
clearInterval(interval); clearInterval(interval);
} }
},1000) },1000)
} }
this.loading = false; this.loading = false;
}, },
error: this.handleHttpError error: this.handleHttpError
} }
); );
} }
confirmCode(event: SubmitEvent): void { confirmCode(event: SubmitEvent): void {
event.preventDefault(); event.preventDefault();
this.jsonrpc.rpc({ this.jsonrpc.rpc({
method: 'getTokenByPhone', method: 'getTokenByPhone',
params: [this.phoneToConfirm, String(this.code)] params: [this.phoneToConfirm, String(this.code)]
}, RpcService.authService, false).subscribe({ }, RpcService.authService, false).subscribe({
next: (result) => { next: (result) => {
if (result.code === 0) { if (result.code === 0) {
this.cookiesService.setCookie('token', result?.data?.token); this.cookiesService.setCookie('token', result?.data?.token);
this.router.navigate(['/auth'], { this.router.navigate(['/auth'], {
queryParams: { queryParams: {
token: result?.data?.token token: result?.data?.token
}, },
}); });
this.phoneConfirmed.emit(null); this.phoneConfirmed.emit(null);
} else { } else {
this.errorConfirmCode = true; this.errorConfirmCode = true;
} }
}, },
error: this.handleHttpError error: this.handleHttpError
} }
); );
} }
} }

View File

@ -1,48 +1,48 @@
<div class="woocommerce-MyAccount-content"> <div class="woocommerce-MyAccount-content">
<h2>{{ currentPage.name }}</h2> <h2>{{ currentPage.name }}</h2>
<!-- <p>{{currentPage.description}}</p> --> <!-- <p>{{currentPage.description}}</p> -->
<div style="display: flex; justify-content: center"> <div style="display: flex; justify-content: center">
<div class="card-container"> <div class="card-container">
<div class="card-container__header"> <div class="card-container__header">
<img src="./assets/logo.svg" alt="Логотип карта" /> <img src="./assets/logo.svg" alt="Логотип карта" />
<span *ngIf="accountData">#{{ accountData.CardNumber }}</span> <span *ngIf="accountData">#{{ accountData.CardNumber }}</span>
</div> </div>
<div class="card-container__decorative-pattern"> <div class="card-container__decorative-pattern">
<!-- <div class="imgs-row"></div> <!-- <div class="imgs-row"></div>
<div class="imgs-row" style="background-position-x: -22px;"></div> <div class="imgs-row" style="background-position-x: -22px;"></div>
<div class="imgs-row"></div> --> <div class="imgs-row"></div> -->
<img src="./assets/card-decorative-pattern.png" alt="" /> <img src="./assets/card-decorative-pattern.png" alt="" />
</div> </div>
<div class="card-container__content"> <div class="card-container__content">
<div class="info"> <div class="info">
<div *ngIf="accountData" class="row"> <div *ngIf="accountData" class="row">
<span class="key">Имя</span> <span class="key">Имя</span>
<span class="value">{{ <span class="value">{{
userName && userName.length ? userName : 'Не задано' userName && userName.length ? userName : 'Не задано'
}}</span> }}</span>
</div> </div>
<div *ngIf="accountData" class="row"> <div *ngIf="accountData" class="row">
<span class="key">Баланс карты</span> <span class="key">Баланс карты</span>
<span class="value">{{ bonuses }}</span> <span class="value">{{ bonuses }}</span>
</div> </div>
</div> </div>
<div class="card-container__barcode-container"> <div class="card-container__barcode-container">
<svg <svg
id="barcode" id="barcode"
[ngClass]="{ [ngClass]="{
hidden: loadingBonuses hidden: loadingBonuses
}" }"
></svg> ></svg>
<p-progressSpinner <p-progressSpinner
*ngIf="loadingBonuses" *ngIf="loadingBonuses"
[style]="{ width: '100%' }" [style]="{ width: '100%' }"
[ngStyle]="{width: '90px'}" [ngStyle]="{width: '90px'}"
strokeWidth="2" strokeWidth="2"
styleClass="angular-spinner" styleClass="angular-spinner"
></p-progressSpinner> ></p-progressSpinner>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<button *ngIf="deviceType == 'ios'" class="add-to-wallet" (click)="addCardToWallet($event)">Добавить в Apple Wallet</button> <button *ngIf="deviceType == 'ios'" class="add-to-wallet" (click)="addCardToWallet($event)">Добавить в Apple Wallet</button>
</div> </div>

View File

@ -1,215 +1,215 @@
:host { :host {
.woocommerce-MyAccount-content { .woocommerce-MyAccount-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
& > h2 { & > h2 {
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 20px; font-size: 20px;
line-height: 24px; line-height: 24px;
max-width: 400px; max-width: 400px;
width: 100%; width: 100%;
margin: 0 auto; margin: 0 auto;
text-align: center; text-align: center;
} }
& > p { & > p {
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 15px; font-size: 15px;
line-height: 18px; line-height: 18px;
margin-top: 8px; margin-top: 8px;
} }
.card-container { .card-container {
background: var(--main-color); background: var(--main-color);
box-shadow: 0px 0px 5px rgb(0 0 0 / 15%); box-shadow: 0px 0px 5px rgb(0 0 0 / 15%);
border-radius: 15px; border-radius: 15px;
height: 466px; height: 466px;
margin-top: 11px; margin-top: 11px;
overflow: hidden; overflow: hidden;
max-width: 400px; max-width: 400px;
width: 100%; width: 100%;
&__header { &__header {
height: 62px; height: 62px;
color: #fff; color: #fff;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
img { img {
height: 38px; height: 38px;
margin-top: 15px; margin-top: 15px;
} }
span { span {
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 20px; font-size: 20px;
line-height: 24px; line-height: 24px;
margin-top: 19px; margin-top: 19px;
margin-right: 18px; margin-right: 18px;
} }
} }
&__decorative-pattern { &__decorative-pattern {
height: 122px; height: 122px;
background-color: #fff; background-color: #fff;
// padding-top: 9px; // padding-top: 9px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
img { img {
height: 90px; height: 90px;
} }
// .imgs-row { // .imgs-row {
// background: url("./assets/card-decorative-pattern.png") repeat-x; // background: url("./assets/card-decorative-pattern.png") repeat-x;
// height: 32%; // height: 32%;
// } // }
} }
&__content { &__content {
background-color: var(--main-color); background-color: var(--main-color);
color: #fff; color: #fff;
.info { .info {
height: 79px; height: 79px;
border-bottom: solid #fff 1px; border-bottom: solid #fff 1px;
padding: 14px 18px; padding: 14px 18px;
.row { .row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 7px; margin-bottom: 7px;
.key { .key {
font-size: 14px; font-size: 14px;
line-height: 17px; line-height: 17px;
font-weight: 400; font-weight: 400;
} }
.value { .value {
font-size: 18px; font-size: 18px;
font-weight: 600; font-weight: 600;
} }
} }
} }
} }
&__barcode-container { &__barcode-container {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
padding: 33px 0; padding: 33px 0;
#barcode { #barcode {
border-radius: 16px; border-radius: 16px;
&.hidden { &.hidden {
display: none; display: none;
width: 0; width: 0;
} }
} }
} }
} }
.card-content { .card-content {
position: relative; position: relative;
height: 200px; height: 200px;
width: 100%; width: 100%;
max-width: 400px; max-width: 400px;
-moz-user-select: none; -moz-user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
-ms-user-select: none; -ms-user-select: none;
-o-user-select: none; -o-user-select: none;
user-select: none; user-select: none;
&__front, &__front,
&__back { &__back {
display: block; display: block;
transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275); transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
transition-duration: 0.7s; transition-duration: 0.7s;
transition-property: transform, opacity; transition-property: transform, opacity;
color: #fff; color: #fff;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: #fdfdfd; background: #fdfdfd;
border-radius: 12px; border-radius: 12px;
border: solid #a3a3a3 1px; border: solid #a3a3a3 1px;
box-shadow: 0 0 3px 1px #fff; box-shadow: 0 0 3px 1px #fff;
padding: 12px; padding: 12px;
background-size: cover; background-size: cover;
} }
// &__front { // &__front {
// transform: rotateY(0deg); // transform: rotateY(0deg);
// background-image: url(./assets/background.svg); // background-image: url(./assets/background.svg);
// } // }
&__back { &__back {
position: absolute; position: absolute;
opacity: 0; opacity: 0;
top: 0px; top: 0px;
left: 0px; left: 0px;
transform: rotateY(-180deg); transform: rotateY(-180deg);
background: #000; background: #000;
} }
&.active_back { &.active_back {
> .card-content__front { > .card-content__front {
transform: rotateY(180deg); transform: rotateY(180deg);
opacity: 0; opacity: 0;
} }
> .card-content__back { > .card-content__back {
opacity: 1; opacity: 1;
transform: rotateY(0deg); transform: rotateY(0deg);
} }
} }
&__logo { &__logo {
height: 30px; height: 30px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
.title { .title {
color: #03d1be; color: #03d1be;
font-weight: 600; font-weight: 600;
letter-spacing: 2px; letter-spacing: 2px;
} }
} }
&__footer { &__footer {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: flex-end; align-items: flex-end;
position: absolute; position: absolute;
bottom: 12px; bottom: 12px;
width: calc(100% - 24px); width: calc(100% - 24px);
.count { .count {
font-size: 22px; font-size: 22px;
} }
} }
} }
.explanation { .explanation {
font-size: 12px; font-size: 12px;
margin-bottom: 8px; margin-bottom: 8px;
} }
.add-to-wallet { .add-to-wallet {
height: 36px; height: 36px;
border-radius: 8px; border-radius: 8px;
background: var(--main-color); background: var(--main-color);
border-color: var(--main-color); border-color: var(--main-color);
color: #fff; color: #fff;
max-width: 400px; max-width: 400px;
width: 100%; width: 100%;
margin: 12px auto 0; margin: 12px auto 0;
} }
} }
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BonusProgramComponent } from './bonus-program.component'; import { BonusProgramComponent } from './bonus-program.component';
describe('BonusProgramComponent', () => { describe('BonusProgramComponent', () => {
let component: BonusProgramComponent; let component: BonusProgramComponent;
let fixture: ComponentFixture<BonusProgramComponent>; let fixture: ComponentFixture<BonusProgramComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ BonusProgramComponent ] declarations: [ BonusProgramComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(BonusProgramComponent); fixture = TestBed.createComponent(BonusProgramComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,193 +1,193 @@
import { Component, Inject, OnInit } from '@angular/core'; import { Component, Inject, OnInit } from '@angular/core';
import { lastValueFrom } from 'rxjs'; import { lastValueFrom } from 'rxjs';
import { import {
orderStatuses, orderStatuses,
PageList, PageList,
PageListWithBonus, PageListWithBonus,
} from 'src/app/app.constants'; } from 'src/app/app.constants';
import { import {
BonusProgramAccount, BonusProgramAccount,
Page, Page,
Purchase, Purchase,
Transaction, Transaction,
} from 'src/app/interface/data'; } from 'src/app/interface/data';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
import * as moment from 'moment-timezone'; import * as moment from 'moment-timezone';
import * as barcode from 'jsbarcode'; import * as barcode from 'jsbarcode';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
import { AppleWalletService } from 'src/app/services/apple-wallet.service'; import { AppleWalletService } from 'src/app/services/apple-wallet.service';
import { CookiesService } from 'src/app/services/cookies.service'; import { CookiesService } from 'src/app/services/cookies.service';
import { DOCUMENT } from '@angular/common'; import { DOCUMENT } from '@angular/common';
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpClient, HttpHeaders } from '@angular/common/http';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
@Component({ @Component({
selector: 'app-bonus-program', selector: 'app-bonus-program',
templateUrl: './bonus-program.component.html', templateUrl: './bonus-program.component.html',
styleUrls: ['./bonus-program.component.scss'], styleUrls: ['./bonus-program.component.scss'],
}) })
export class BonusProgramComponent implements OnInit { export class BonusProgramComponent implements OnInit {
public accountData!: BonusProgramAccount; public accountData!: BonusProgramAccount;
public purchases: Purchase[] = []; public purchases: Purchase[] = [];
public loadingBonuses: boolean = false; public loadingBonuses: boolean = false;
readonly orderStatuses = orderStatuses; readonly orderStatuses = orderStatuses;
readonly moment = moment; readonly moment = moment;
readonly pageList = environment.hasBonusProgram readonly pageList = environment.hasBonusProgram
? PageListWithBonus ? PageListWithBonus
: PageList; : PageList;
public currentPage: Page = this.pageList[1]; public currentPage: Page = this.pageList[1];
public userName: string = ''; public userName: string = '';
public deviceType: 'ios' | 'android' | null = null; public deviceType: 'ios' | 'android' | null = null;
public bonuses: number = 0 public bonuses: number = 0
constructor( constructor(
private jsonrpc: JsonrpcService, private jsonrpc: JsonrpcService,
private appleWallet: AppleWalletService, private appleWallet: AppleWalletService,
private cookiesService: CookiesService, private cookiesService: CookiesService,
@Inject(DOCUMENT) private document: Document, @Inject(DOCUMENT) private document: Document,
private http: HttpClient, private http: HttpClient,
private messageService: MessageService, private messageService: MessageService,
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
this.getAccountData(); this.getAccountData();
this.getTypeDevice(); this.getTypeDevice();
} }
getTypeDevice() { getTypeDevice() {
const userAgent = window.navigator.userAgent.toLowerCase(); const userAgent = window.navigator.userAgent.toLowerCase();
const ios = /iphone|ipod|ipad/.test(userAgent); const ios = /iphone|ipod|ipad/.test(userAgent);
this.deviceType = ios ? 'ios' : 'android'; this.deviceType = ios ? 'ios' : 'android';
} }
async getAccountData(): Promise<void> { async getAccountData(): Promise<void> {
this.loadingBonuses = true; this.loadingBonuses = true;
this.jsonrpc this.jsonrpc
.rpc( .rpc(
{ {
method: 'getAdditionalInfo', method: 'getAdditionalInfo',
params: [], params: [],
}, },
RpcService.authService, RpcService.authService,
true true
) )
.subscribe({ .subscribe({
next: (res) => { next: (res) => {
this.userName = res?.data?.first_name; this.userName = res?.data?.first_name;
}, },
error: (err) => { error: (err) => {
console.error('Error: ', err); console.error('Error: ', err);
}, },
}); });
const getAccount = await lastValueFrom( const getAccount = await lastValueFrom(
this.jsonrpc.rpc( this.jsonrpc.rpc(
{ {
method: 'GetAccounts', method: 'GetAccounts',
params: [], params: [],
}, },
RpcService.bonusService RpcService.bonusService
) )
); );
this.accountData = getAccount && getAccount['Cards'][0]; this.accountData = getAccount && getAccount['Cards'][0];
this.bonuses = this.accountData.BonusProgramAccounts?.reduce((previous, {Bonuses}) => { this.bonuses = this.accountData.BonusProgramAccounts?.reduce((previous, {Bonuses}) => {
return previous + Bonuses return previous + Bonuses
}, 0) || this.accountData.Bonuses }, 0) || this.accountData.Bonuses
this.loadingBonuses = false; this.loadingBonuses = false;
if (this.accountData) { if (this.accountData) {
barcode('#barcode') barcode('#barcode')
.options({ font: 'OCR-B' }) // Will affect all barcodes .options({ font: 'OCR-B' }) // Will affect all barcodes
.EAN13(`${this.accountData.CardNumber}`.padStart(12, '0'), { .EAN13(`${this.accountData.CardNumber}`.padStart(12, '0'), {
fontSize: 18, fontSize: 18,
textMargin: 0, textMargin: 0,
}) })
.render(); .render();
} }
const getTransactions = ( const getTransactions = (
await lastValueFrom( await lastValueFrom(
this.jsonrpc.rpc( this.jsonrpc.rpc(
{ {
method: 'GetAccountTransactions', method: 'GetAccountTransactions',
params: [], params: [],
}, },
RpcService.bonusService RpcService.bonusService
) )
) )
) )
if (!getTransactions) { if (!getTransactions) {
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'}); this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
return return
} }
const transactions: Transaction[] = getTransactions && getTransactions['Transactions']; const transactions: Transaction[] = getTransactions && getTransactions['Transactions'];
const getPurchases = ( const getPurchases = (
await lastValueFrom( await lastValueFrom(
this.jsonrpc.rpc( this.jsonrpc.rpc(
{ {
method: 'GetAccountPurchase', method: 'GetAccountPurchase',
params: [], params: [],
}, },
RpcService.bonusService RpcService.bonusService
) )
) )
) )
if (!getPurchases) { if (!getPurchases) {
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'}); this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
return return
} }
const purchases: Purchase[] = getPurchases && getPurchases['Purchases']; const purchases: Purchase[] = getPurchases && getPurchases['Purchases'];
this.purchases = purchases && purchases.map<Purchase>((purchase) => { this.purchases = purchases && purchases.map<Purchase>((purchase) => {
const id = purchase.ID.slice(0, 36).toLowerCase(); const id = purchase.ID.slice(0, 36).toLowerCase();
purchase.Transactions = transactions.filter((transaction) => { purchase.Transactions = transactions.filter((transaction) => {
const same = transaction.Purchase === id; const same = transaction.Purchase === id;
transaction.HasPurchase = same; transaction.HasPurchase = same;
return same; return same;
}); });
return purchase; return purchase;
}); });
transactions && transactions.forEach((transaction) => { transactions && transactions.forEach((transaction) => {
if (!transaction.HasPurchase) { if (!transaction.HasPurchase) {
this.purchases.push({ this.purchases.push({
ID: transaction.ID, ID: transaction.ID,
PurchaseDate: transaction.Date, PurchaseDate: transaction.Date,
Transactions: [transaction], Transactions: [transaction],
IsSingleTransaction: true, IsSingleTransaction: true,
transactionSum: 0 transactionSum: 0
}); });
} }
}); });
this.purchases = this.purchases && this.purchases.sort((a, b) => { this.purchases = this.purchases && this.purchases.sort((a, b) => {
return moment(a.PurchaseDate).date() - moment(b.PurchaseDate).date(); return moment(a.PurchaseDate).date() - moment(b.PurchaseDate).date();
}); });
} }
async addCardToWallet(e: any) { async addCardToWallet(e: any) {
e.preventDefault(); e.preventDefault();
const token = this.cookiesService.getItem('token'); const token = this.cookiesService.getItem('token');
const accountData = ( const accountData = (
await lastValueFrom( await lastValueFrom(
this.jsonrpc.rpc( this.jsonrpc.rpc(
{ {
method: 'getTokenData', method: 'getTokenData',
params: [], params: [],
}, },
RpcService.authService, RpcService.authService,
true true
) )
) )
).data; ).data;
if (token && accountData.user_id) { if (token && accountData.user_id) {
this.appleWallet.generateCard(token, accountData.user_id).subscribe({ this.appleWallet.generateCard(token, accountData.user_id).subscribe({
next: (res: any) => { next: (res: any) => {
this.document.location.href = res.url; this.document.location.href = res.url;
}, },
error: (err) => { error: (err) => {
console.log('Error: ', err); console.log('Error: ', err);
}, },
}); });
} }
} }
} }

View File

@ -1,89 +1,89 @@
<div class="orders"> <div class="orders">
<table <table
class="woocommerce-orders-table woocommerce-MyAccount-orders shop_table shop_table_responsive my_account_orders account-orders-table" class="woocommerce-orders-table woocommerce-MyAccount-orders shop_table shop_table_responsive my_account_orders account-orders-table"
> >
<thead> <thead>
<tr> <tr>
<th <th
class="woocommerce-orders-table__header woocommerce-orders-table__header-order-date" class="woocommerce-orders-table__header woocommerce-orders-table__header-order-date"
> >
<span class="nobr">Дата</span> <span class="nobr">Дата</span>
</th> </th>
<th <th
class="woocommerce-orders-table__header woocommerce-orders-table__header-order-status" class="woocommerce-orders-table__header woocommerce-orders-table__header-order-status"
> >
<span class="nobr">Место покупки</span> <span class="nobr">Место покупки</span>
</th> </th>
<th <th
class="woocommerce-orders-table__header woocommerce-orders-table__header-order-total" class="woocommerce-orders-table__header woocommerce-orders-table__header-order-total"
> >
<span class="nobr">Сумма чека</span> <span class="nobr">Сумма чека</span>
</th> </th>
<th <th
class="woocommerce-orders-table__header woocommerce-orders-table__header-order-actions" class="woocommerce-orders-table__header woocommerce-orders-table__header-order-actions"
> >
<span class="nobr">Бонусы</span> <span class="nobr">Бонусы</span>
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr <tr
*ngFor="let purchase of purchasesShortArray" *ngFor="let purchase of purchasesShortArray"
class="woocommerce-orders-table__row woocommerce-orders-table__row--status-processing order" class="woocommerce-orders-table__row woocommerce-orders-table__row--status-processing order"
> >
<td <td
class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-date" class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-date"
data-title="Дата" data-title="Дата"
> >
<time [dateTime]="purchase.PurchaseDate">{{ <time [dateTime]="purchase.PurchaseDate">{{
moment(purchase.PurchaseDate).format("DD.MM.YYYY") moment(purchase.PurchaseDate).format("DD.MM.YYYY")
}}</time> }}</time>
</td> </td>
<td <td
class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-status" class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-status"
data-title="Место покупки" data-title="Место покупки"
> >
{{ purchase.Address }} {{ purchase.Address }}
</td> </td>
<td <td
class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-total" class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-total"
data-title="Сумма чека" data-title="Сумма чека"
> >
<span class="woocommerce-Price-amount amount"> <span class="woocommerce-Price-amount amount">
{{ purchase.CheckSummary }} {{ purchase.CheckSummary }}
</span> </span>
</td> </td>
<td <td
class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-actions" class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-actions"
data-title="Бонусы" data-title="Бонусы"
[ngClass]="{ [ngClass]="{
'red-color': purchase.BonusSummary ? purchase.BonusSummary < 0 : false, 'red-color': purchase.BonusSummary ? purchase.BonusSummary < 0 : false,
'green-color': purchase.BonusSummary ? purchase.BonusSummary > 0 : false 'green-color': purchase.BonusSummary ? purchase.BonusSummary > 0 : false
}" }"
> >
{{ purchase.BonusSummary }} {{ purchase.BonusSummary }}
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<p <p
*ngIf="purchases?.length !== purchasesShortArray?.length" *ngIf="purchases?.length !== purchasesShortArray?.length"
class="show-more-orders" class="show-more-orders"
(click)="getMoreOrders()" (click)="getMoreOrders()"
> >
Показать больше Показать больше
</p> </p>
<p-progressSpinner <p-progressSpinner
*ngIf="ordersLoadingStatus" *ngIf="ordersLoadingStatus"
[style]="{ display: 'block' }" [style]="{ display: 'block' }"
strokeWidth="2" strokeWidth="2"
styleClass="angular-spinner" styleClass="angular-spinner"
></p-progressSpinner> ></p-progressSpinner>
<p <p
*ngIf="purchases?.length === 0 && !ordersLoadingStatus" *ngIf="purchases?.length === 0 && !ordersLoadingStatus"
class="no-orders" class="no-orders"
style="width: 100%; text-align: center" style="width: 100%; text-align: center"
> >
Нет заказов Нет заказов
</p> </p>
</div> </div>

View File

@ -1,76 +1,76 @@
.show-more-orders { .show-more-orders {
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
color: var(--main-color); color: var(--main-color);
margin-top: 16px; margin-top: 16px;
} }
.woocommerce-MyAccount-content { .woocommerce-MyAccount-content {
max-width: 400px; max-width: 400px;
margin-left: calc(50vw - 200px); margin-left: calc(50vw - 200px);
} }
.woocommerce-orders-table { .woocommerce-orders-table {
border: 2px solid #dee2e6; border: 2px solid #dee2e6;
border-bottom: 0; border-bottom: 0;
border-radius: 0.25rem; border-radius: 0.25rem;
border-collapse: separate; border-collapse: separate;
text-align: left; text-align: left;
margin-top: 8px; margin-top: 8px;
width: 100%; width: 100%;
max-width: 400px; max-width: 400px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
thead { thead {
display: none; display: none;
} }
.woocommerce-orders-table { .woocommerce-orders-table {
&__cell { &__cell {
padding: 0.7rem 1.5rem; padding: 0.7rem 1.5rem;
border-bottom: 2px solid #dee2e6; border-bottom: 2px solid #dee2e6;
text-align: right !important; text-align: right !important;
display: block; display: block;
&::before { &::before {
content: attr(data-title) ": "; content: attr(data-title) ": ";
font-weight: 700; font-weight: 700;
float: left; float: left;
} }
} }
&__row { &__row {
display: block; display: block;
&:nth-child(even) { &:nth-child(even) {
background: #ebebeb; background: #ebebeb;
} }
} }
// &__cell-order-actions::before { // &__cell-order-actions::before {
// display: none; // display: none;
// } // }
&__cell-order-actions { &__cell-order-actions {
&.red-color { &.red-color {
color: #ed0000; color: #ed0000;
} }
&.green-color { &.green-color {
color: #00a700; color: #00a700;
} }
} }
&__cell-order-number a { &__cell-order-number a {
text-decoration: none; text-decoration: none;
color: #009688; color: #009688;
} }
} }
.woocommerce-button { .woocommerce-button {
display: inline-block; display: inline-block;
color: #ffffff; color: #ffffff;
background-color: #009688; background-color: #009688;
padding: 13px; padding: 13px;
border-radius: 4px; border-radius: 4px;
text-decoration: none; text-decoration: none;
width: 100%; width: 100%;
text-align: center; text-align: center;
} }
} }
.no-orders { .no-orders {
width: 100%; width: 100%;
text-align: center; text-align: center;
margin-top: 16px; margin-top: 16px;
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OrdersComponent } from './orders.component'; import { OrdersComponent } from './orders.component';
describe('OrdersComponent', () => { describe('OrdersComponent', () => {
let component: OrdersComponent; let component: OrdersComponent;
let fixture: ComponentFixture<OrdersComponent>; let fixture: ComponentFixture<OrdersComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ OrdersComponent ] declarations: [ OrdersComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(OrdersComponent); fixture = TestBed.createComponent(OrdersComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,67 +1,67 @@
import { HttpErrorResponse } from '@angular/common/http'; import { HttpErrorResponse } from '@angular/common/http';
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Purchase } from 'src/app/interface/data'; import { Purchase } from 'src/app/interface/data';
import * as moment from 'moment-timezone'; import * as moment from 'moment-timezone';
import { lastValueFrom } from 'rxjs'; import { lastValueFrom } from 'rxjs';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
@Component({ @Component({
selector: 'app-orders', selector: 'app-orders',
templateUrl: './orders.component.html', templateUrl: './orders.component.html',
styleUrls: ['./orders.component.scss'], styleUrls: ['./orders.component.scss'],
}) })
export class OrdersComponent implements OnInit { export class OrdersComponent implements OnInit {
@Input() handleHttpError!: (error: HttpErrorResponse) => void; @Input() handleHttpError!: (error: HttpErrorResponse) => void;
public lastViewOrder: number = 3; public lastViewOrder: number = 3;
public ordersLoadingStatus: boolean = true; public ordersLoadingStatus: boolean = true;
readonly moment = moment; readonly moment = moment;
public purchases: Purchase[] = []; public purchases: Purchase[] = [];
public purchasesShortArray: Purchase[] = []; public purchasesShortArray: Purchase[] = [];
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
private jsonrpc: JsonrpcService, private jsonrpc: JsonrpcService,
private messageService: MessageService, private messageService: MessageService,
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
this.getOrders(); this.getOrders();
} }
async getOrders() { async getOrders() {
const getPurchases = await lastValueFrom( const getPurchases = await lastValueFrom(
this.jsonrpc.rpc( this.jsonrpc.rpc(
{ {
method: 'GetAccountPurchase', method: 'GetAccountPurchase',
params: [], params: [],
}, },
RpcService.bonusService RpcService.bonusService
) )
); );
if (!getPurchases) { if (!getPurchases) {
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'}); this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
return return
} }
const purchases: Purchase[] = getPurchases && getPurchases['Purchases']; const purchases: Purchase[] = getPurchases && getPurchases['Purchases'];
this.purchases = purchases && purchases.map<Purchase>((purchase) => { this.purchases = purchases && purchases.map<Purchase>((purchase) => {
const id = purchase.ID.slice(0, 36).toLowerCase(); const id = purchase.ID.slice(0, 36).toLowerCase();
// purchase.Transactions = transactions.filter((transaction) => { // purchase.Transactions = transactions.filter((transaction) => {
// const same = transaction.Purchase === id; // const same = transaction.Purchase === id;
// transaction.HasPurchase = same; // transaction.HasPurchase = same;
// return same; // return same;
// }); // });
return purchase; return purchase;
}); });
this.purchasesShortArray = this.purchases && this.purchases.slice(0, this.lastViewOrder); this.purchasesShortArray = this.purchases && this.purchases.slice(0, this.lastViewOrder);
this.ordersLoadingStatus = false; this.ordersLoadingStatus = false;
} }
getMoreOrders() { getMoreOrders() {
this.lastViewOrder += 4; this.lastViewOrder += 4;
this.purchasesShortArray = this.purchases.slice(0, this.lastViewOrder); this.purchasesShortArray = this.purchases.slice(0, this.lastViewOrder);
} }
} }

View File

@ -1,25 +1,25 @@
<div class="ref-system"> <div class="ref-system">
<!-- <qrcode <!-- <qrcode
*ngIf="!loading" *ngIf="!loading"
[qrdata]="refUrl" [qrdata]="refUrl"
[width]="256" [width]="256"
[errorCorrectionLevel]="'M'" [errorCorrectionLevel]="'M'"
></qrcode> --> ></qrcode> -->
<p-progressSpinner <p-progressSpinner
*ngIf="loading" *ngIf="loading"
[style]="{ width: '100%' }" [style]="{ width: '100%' }"
strokeWidth="2" strokeWidth="2"
styleClass="angular-spinner" styleClass="angular-spinner"
></p-progressSpinner> ></p-progressSpinner>
<!-- <share-buttons <!-- <share-buttons
[theme]="'modern-dark'" [theme]="'modern-dark'"
[include]="['telegram', 'whatsapp', 'vk', 'copy', 'facebook', 'twitter']" [include]="['telegram', 'whatsapp', 'vk', 'copy', 'facebook', 'twitter']"
[show]="4" [show]="4"
[url]="refUrl" [url]="refUrl"
style="display: flex; justify-content: center;" style="display: flex; justify-content: center;"
></share-buttons> --> ></share-buttons> -->
<div class="share-container" *ngIf="!loading"> <div class="share-container" *ngIf="!loading">
<div class="copy" type="button" (click)="copyUrl()">Скопировать ссылку</div> <div class="copy" type="button" (click)="copyUrl()">Скопировать ссылку</div>
<div class="share" type="button" (click)="share()"><img src="./assets/share.svg" alt="share"></div> <div class="share" type="button" (click)="share()"><img src="./assets/share.svg" alt="share"></div>
</div> </div>
</div> </div>

View File

@ -1,33 +1,33 @@
:host { :host {
.ref-system { .ref-system {
.share-container { .share-container {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
height: 50px; height: 50px;
max-width: 400px; max-width: 400px;
margin: 0 auto; margin: 0 auto;
.share { .share {
width: 50px; width: 50px;
background-color: var(--main-color); background-color: var(--main-color);
border-radius: 8px; border-radius: 8px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
img { img {
height: 26px; height: 26px;
} }
} }
.copy { .copy {
background-color: var(--main-color); background-color: var(--main-color);
color: #fff; color: #fff;
border-radius: 8px; border-radius: 8px;
width: calc(100% - 66px); width: calc(100% - 66px);
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
} }
} }
} }
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RefSystemComponent } from './ref-system.component'; import { RefSystemComponent } from './ref-system.component';
describe('RefSystemComponent', () => { describe('RefSystemComponent', () => {
let component: RefSystemComponent; let component: RefSystemComponent;
let fixture: ComponentFixture<RefSystemComponent>; let fixture: ComponentFixture<RefSystemComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ RefSystemComponent ] declarations: [ RefSystemComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(RefSystemComponent); fixture = TestBed.createComponent(RefSystemComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,71 +1,71 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import * as barcode from 'jsbarcode'; import * as barcode from 'jsbarcode';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { lastValueFrom } from 'rxjs'; import { lastValueFrom } from 'rxjs';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
@Component({ @Component({
selector: 'app-ref-system', selector: 'app-ref-system',
templateUrl: './ref-system.component.html', templateUrl: './ref-system.component.html',
styleUrls: ['./ref-system.component.scss'] styleUrls: ['./ref-system.component.scss']
}) })
export class RefSystemComponent implements OnInit { export class RefSystemComponent implements OnInit {
public refUrl: string = `${environment.defaultUrl}/?refUserId=` public refUrl: string = `${environment.defaultUrl}/?refUserId=`
public loading: boolean = true; public loading: boolean = true;
constructor( constructor(
private jsonrpc: JsonrpcService, private jsonrpc: JsonrpcService,
private messageService: MessageService private messageService: MessageService
) { } ) { }
async ngOnInit() { async ngOnInit() {
const accountData = (await lastValueFrom( const accountData = (await lastValueFrom(
this.jsonrpc this.jsonrpc
.rpc( .rpc(
{ {
method: 'getTokenData', method: 'getTokenData',
params: [], params: [],
}, },
RpcService.authService, RpcService.authService,
true true
) )
)).data )).data
this.refUrl += accountData.user_id this.refUrl += accountData.user_id
this.loading = false this.loading = false
} }
share() { share() {
if (navigator.share) { if (navigator.share) {
navigator.share({ navigator.share({
title: document.title, title: document.title,
text: "Tasty Coffee", text: "Tasty Coffee",
url: this.refUrl url: this.refUrl
}) })
.then(() => console.log('Successful share')) .then(() => console.log('Successful share'))
.catch((error) => { .catch((error) => {
console.log('Error sharing:', error) console.log('Error sharing:', error)
}); });
} }
} }
copyUrl() { copyUrl() {
navigator.clipboard.writeText(this.refUrl) navigator.clipboard.writeText(this.refUrl)
.then(() => { .then(() => {
this.messageService.clear(); this.messageService.clear();
this.messageService.add({ this.messageService.add({
severity: 'custom', severity: 'custom',
summary: 'Ссылка скопирована!', summary: 'Ссылка скопирована!',
}); });
}) })
.catch(err => { .catch(err => {
this.messageService.clear(); this.messageService.clear();
this.messageService.add({ this.messageService.add({
severity: 'error', severity: 'error',
summary: 'Произошла ошибка!', summary: 'Произошла ошибка!',
}); });
}); });
} }
} }

View File

@ -1,87 +1,87 @@
<form <form
class="woocommerce-form woocommerce-form-login login" class="woocommerce-form woocommerce-form-login login"
action="false" action="false"
(ngSubmit)="saveData()" (ngSubmit)="saveData()"
[formGroup]="userDataFormGroup" [formGroup]="userDataFormGroup"
> >
<h2>Личные данные</h2> <h2>Личные данные</h2>
<p <p
class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide" class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide"
> >
<label for="phone">Заполните поля анкеты</label> <label for="phone">Заполните поля анкеты</label>
<input <input
formControlName="first_name" formControlName="first_name"
pInputText pInputText
type="text" type="text"
class="woocommerce-Input woocommerce-Input--text input-text" class="woocommerce-Input woocommerce-Input--text input-text"
name="first_name" name="first_name"
id="first_name" id="first_name"
placeholder="Имя" placeholder="Имя"
/> />
</p> </p>
<p <p
class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide" class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide"
> >
<!-- <p-inputMask <!-- <p-inputMask
type="text" type="text"
class="woocommerce-Input woocommerce-Input--text" class="woocommerce-Input woocommerce-Input--text"
name="birthdate" name="birthdate"
inputId="birthdate" inputId="birthdate"
placeholder="Дата рождения (00.00.00)" placeholder="Дата рождения (00.00.00)"
mask="99.99.99" mask="99.99.99"
[styleClass]="datevalid ? '' : 'invalid'" [styleClass]="datevalid ? '' : 'invalid'"
[(ngModel)]="birthdate" [(ngModel)]="birthdate"
[ngModelOptions]="{standalone: true}" [ngModelOptions]="{standalone: true}"
></p-inputMask> --> ></p-inputMask> -->
<input <input
type="date" type="date"
formControlName="birthdate" formControlName="birthdate"
class="woocommerce-Input woocommerce-Input--text input-text" class="woocommerce-Input woocommerce-Input--text input-text"
pInputText pInputText
name="birthdate" name="birthdate"
id="birthdate" id="birthdate"
placeholder="Дата рождения" placeholder="Дата рождения"
/> />
</p> </p>
<p <p
class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide" class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide"
> >
<span class="select-wrapper" [ngClass]="{ <span class="select-wrapper" [ngClass]="{
'select-wrapper': true, 'select-wrapper': true,
'hidden': userDataFormGroup.value.gender.length 'hidden': userDataFormGroup.value.gender.length
}"> }">
<select formControlName="gender" name="gender" id="gender" required> <select formControlName="gender" name="gender" id="gender" required>
<!-- <option hidden value="" disabled selected>Выберите пол</option> --> <!-- <option hidden value="" disabled selected>Выберите пол</option> -->
<option value="Мужской">Мужской</option> <option value="Мужской">Мужской</option>
<option value="Женский">Женский</option> <option value="Женский">Женский</option>
</select> </select>
</span> </span>
</p> </p>
<p class="form-row" style="margin-bottom: 6px"> <p class="form-row" style="margin-bottom: 6px">
<button <button
type="submit" type="submit"
class="woocommerce-button button woocommerce-form-login__submit" class="woocommerce-button button woocommerce-form-login__submit"
name="save" name="save"
value="Сохранить" value="Сохранить"
[disabled]="false" [disabled]="false"
> >
Сохранить Сохранить
</button> </button>
</p> </p>
<span class="policy" <span class="policy"
>Нажимая кнопку, Вы соглашаетесь с Политикой обработки персональных >Нажимая кнопку, Вы соглашаетесь с Политикой обработки персональных
данных.</span данных.</span
> >
<div class="decoration-pattern"> <div class="decoration-pattern">
<img <img
style="height: 34px" style="height: 34px"
src="./assets/card-decorative-pattern.png" src="./assets/card-decorative-pattern.png"
alt="" alt=""
/> />
<img <img
style="height: 16px; margin-left: 2px" style="height: 16px; margin-left: 2px"
src="./assets/card-decorative-pattern.png" src="./assets/card-decorative-pattern.png"
alt="" alt=""
/> />
</div> </div>
</form> </form>

View File

@ -1,189 +1,189 @@
:host { :host {
display: flex; display: flex;
justify-content: center; justify-content: center;
.woocommerce-form-login__submit { .woocommerce-form-login__submit {
width: 150px; width: 150px;
} }
h2 { h2 {
color: #fff; color: #fff;
width: 100%; width: 100%;
height: 49px; height: 49px;
text-align: left; text-align: left;
padding-top: 13px; padding-top: 13px;
padding-left: 32px; padding-left: 32px;
background: #0d457e; background: #0d457e;
position: relative; position: relative;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 20px; font-size: 20px;
line-height: 24px; line-height: 24px;
&::before { &::before {
content: ""; content: "";
width: 19px; width: 19px;
height: 4px; height: 4px;
background: #fff; background: #fff;
display: block; display: block;
position: absolute; position: absolute;
left: 0; left: 0;
top: 24px; top: 24px;
} }
} }
.woocommerce-form { .woocommerce-form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
background: #ffffff; background: #ffffff;
box-shadow: 0px 0px 10px rgb(0 0 0 / 15%); box-shadow: 0px 0px 10px rgb(0 0 0 / 15%);
border-radius: 15px; border-radius: 15px;
min-height: 262px; min-height: 262px;
max-width: 400px; max-width: 400px;
overflow: auto; overflow: auto;
position: relative; position: relative;
padding-bottom: 10px; padding-bottom: 10px;
} }
.form-row { .form-row {
width: 80%; width: 80%;
margin-bottom: 16px; margin-bottom: 16px;
max-width: 400px; max-width: 400px;
.button { .button {
width: 100%; width: 100%;
font-weight: 600; font-weight: 600;
letter-spacing: 2px; letter-spacing: 2px;
border: none; border: none;
height: 45px; height: 45px;
background: #b8deff; background: #b8deff;
border-radius: 15px; border-radius: 15px;
color: #000; color: #000;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 16px; font-size: 16px;
line-height: 20px; line-height: 20px;
} }
label { label {
display: inline-block; display: inline-block;
margin-bottom: 8px; margin-bottom: 8px;
margin-top: 15px; margin-top: 15px;
color: #000; color: #000;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 18px; font-size: 18px;
line-height: 22px; line-height: 22px;
} }
.input-text { .input-text {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol"; "Segoe UI Symbol";
color: #495057; color: #495057;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
padding-left: 13px; padding-left: 13px;
transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; appearance: none;
border-radius: 4px; border-radius: 4px;
width: 100%; width: 100%;
height: 45px; height: 45px;
background: #FFFFFF; background: #FFFFFF;
border: 1px solid #B8DEFF; border: 1px solid #B8DEFF;
border-radius: 15px; border-radius: 15px;
font-size: 14px; font-size: 14px;
text-align: left; text-align: left;
&.ng-dirty.ng-invalid { &.ng-dirty.ng-invalid {
border-color: red; border-color: red;
} }
&.ng-invalid.ng-touched { &.ng-invalid.ng-touched {
border-color: red; border-color: red;
} }
} }
select { select {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol"; "Segoe UI Symbol";
background: #FFFFFF; background: #FFFFFF;
border: 1px solid #B8DEFF; border: 1px solid #B8DEFF;
border-radius: 15px; border-radius: 15px;
width: 100%; width: 100%;
height: 45px; height: 45px;
padding: 0 10px; padding: 0 10px;
font-size: 14px; font-size: 14px;
text-align: left; text-align: left;
color: #495057; color: #495057;
// option[value=""][disabled] { // option[value=""][disabled] {
// display: none; // display: none;
// } // }
&.ng-dirty.ng-invalid { &.ng-dirty.ng-invalid {
border-color: red; border-color: red;
} }
&.ng-invalid.ng-touched { &.ng-invalid.ng-touched {
border-color: red; border-color: red;
} }
} }
select:required:invalid { select:required:invalid {
color: #6c757d; color: #6c757d;
} }
} }
.policy { .policy {
width: 80%; width: 80%;
text-align: center; text-align: center;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 15px; line-height: 15px;
color: #BDBDBD; color: #BDBDBD;
} }
.decoration-pattern { .decoration-pattern {
margin-top: 12px; margin-top: 12px;
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
margin-left: 0; margin-left: 0;
width: 92%; width: 92%;
} }
input[type="date"]::before { input[type="date"]::before {
content: attr(placeholder); content: attr(placeholder);
width: 100%; width: 100%;
color: #6c757d; color: #6c757d;
} }
input[type="date"]:focus::before, input[type="date"]:focus::before,
input[type="date"].ng-valid::before { input[type="date"].ng-valid::before {
display: none; display: none;
} }
.select-wrapper { .select-wrapper {
position: relative; position: relative;
&::before { &::before {
content: 'Выберите пол'; content: 'Выберите пол';
position: absolute; position: absolute;
top: 3px; top: 3px;
left: 13px; left: 13px;
height: 100%; height: 100%;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol" !important; "Segoe UI Symbol" !important;
font-family: inherit; font-family: inherit;
color: #6c757d; color: #6c757d;
font-size: 14px; font-size: 14px;
} }
&.hidden::before { &.hidden::before {
display: none; display: none;
} }
} }
/* hide our custom/fake placeholder text when in focus to show the default /* hide our custom/fake placeholder text when in focus to show the default
* 'mm/dd/yyyy' value and when valid to show the users' date of birth value. * 'mm/dd/yyyy' value and when valid to show the users' date of birth value.
*/ */
// input[type="date"]:focus::before, // input[type="date"]:focus::before,
// input[type="date"]:valid::before { display: none } // input[type="date"]:valid::before { display: none }
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UserDataComponent } from './user-data.component'; import { UserDataComponent } from './user-data.component';
describe('UserDataComponent', () => { describe('UserDataComponent', () => {
let component: UserDataComponent; let component: UserDataComponent;
let fixture: ComponentFixture<UserDataComponent>; let fixture: ComponentFixture<UserDataComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ UserDataComponent ] declarations: [ UserDataComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(UserDataComponent); fixture = TestBed.createComponent(UserDataComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,110 +1,110 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { lastValueFrom } from 'rxjs'; import { lastValueFrom } from 'rxjs';
import { UserDataForm } from 'src/app/interface/data'; import { UserDataForm } from 'src/app/interface/data';
import { AppleWalletService } from 'src/app/services/apple-wallet.service'; import { AppleWalletService } from 'src/app/services/apple-wallet.service';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
@Component({ @Component({
selector: 'app-user-data', selector: 'app-user-data',
templateUrl: './user-data.component.html', templateUrl: './user-data.component.html',
styleUrls: ['./user-data.component.scss'] styleUrls: ['./user-data.component.scss']
}) })
export class UserDataComponent implements OnInit { export class UserDataComponent implements OnInit {
public userDataFormGroup!: FormGroup; public userDataFormGroup!: FormGroup;
public userData: UserDataForm = { public userData: UserDataForm = {
first_name: '', first_name: '',
birthdate: '', birthdate: '',
gender: '', gender: '',
} }
constructor( constructor(
private jsonRpcService: JsonrpcService, private jsonRpcService: JsonrpcService,
private messageService: MessageService, private messageService: MessageService,
private appleWallet: AppleWalletService, private appleWallet: AppleWalletService,
private jsonrpc: JsonrpcService, private jsonrpc: JsonrpcService,
) { } ) { }
ngOnInit(): void { ngOnInit(): void {
this.createUserDataForm({...this.userData}) this.createUserDataForm({...this.userData})
this.jsonRpcService.rpc({ this.jsonRpcService.rpc({
method: 'getAdditionalInfo', method: 'getAdditionalInfo',
params: [] params: []
}, RpcService.authService, true).subscribe({ }, RpcService.authService, true).subscribe({
next: (res) => { next: (res) => {
if (res.code === 45) { if (res.code === 45) {
this.messageService.add({severity:'warn', summary:'Данные не найдены, заполните их на этой странице'}); this.messageService.add({severity:'warn', summary:'Данные не найдены, заполните их на этой странице'});
return return
} }
if (!res.data) { if (!res.data) {
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'}); this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
return return
} }
const { first_name, birthdate, gender } = res.data const { first_name, birthdate, gender } = res.data
this.userData = { first_name, birthdate, gender } this.userData = { first_name, birthdate, gender }
this.createUserDataForm({...this.userData}) this.createUserDataForm({...this.userData})
}, },
error: (err) => { error: (err) => {
console.error('Error: ', err) console.error('Error: ', err)
} }
}); });
} }
createUserDataForm(val: UserDataForm) { createUserDataForm(val: UserDataForm) {
this.userDataFormGroup = new FormGroup({ this.userDataFormGroup = new FormGroup({
first_name: new FormControl(val.first_name, [Validators.required, Validators.minLength(2)]), first_name: new FormControl(val.first_name, [Validators.required, Validators.minLength(2)]),
birthdate: new FormControl(val.birthdate, [Validators.required]), birthdate: new FormControl(val.birthdate, [Validators.required]),
gender: new FormControl(val.gender ? val.gender : '', [Validators.required]), gender: new FormControl(val.gender ? val.gender : '', [Validators.required]),
}) })
} }
saveData() { saveData() {
if (this.userDataFormGroup.invalid) { if (this.userDataFormGroup.invalid) {
this.markFormGroupTouched(this.userDataFormGroup) this.markFormGroupTouched(this.userDataFormGroup)
return return
} }
this.userData = this.userDataFormGroup.value as UserDataForm this.userData = this.userDataFormGroup.value as UserDataForm
this.jsonRpcService.rpc({ this.jsonRpcService.rpc({
method: 'updateAdditionalInfo', method: 'updateAdditionalInfo',
params: [this.userData] params: [this.userData]
}, RpcService.authService, true).subscribe({ }, RpcService.authService, true).subscribe({
next: async () => { next: async () => {
this.messageService.add({severity:'custom', summary:'Данные успешно обновлены!'}); this.messageService.add({severity:'custom', summary:'Данные успешно обновлены!'});
const accountData = (await lastValueFrom( const accountData = (await lastValueFrom(
this.jsonrpc this.jsonrpc
.rpc( .rpc(
{ {
method: 'getTokenData', method: 'getTokenData',
params: [], params: [],
}, },
RpcService.authService, RpcService.authService,
true true
) )
)).data )).data
if (accountData.user_id) { if (accountData.user_id) {
this.appleWallet.reloadCard(accountData.user_id).subscribe({ this.appleWallet.reloadCard(accountData.user_id).subscribe({
next: (value) => { next: (value) => {
console.log(value); console.log(value);
}, },
error: (err) => { error: (err) => {
console.error(err); console.error(err);
} }
}) })
} }
}, },
error: (err) => { error: (err) => {
console.error('Error: ', err) console.error('Error: ', err)
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'}); this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
} }
}) })
} }
markFormGroupTouched(formGroup: FormGroup) { markFormGroupTouched(formGroup: FormGroup) {
(<any>Object).values(formGroup.controls).forEach((control: FormControl) => { (<any>Object).values(formGroup.controls).forEach((control: FormControl) => {
control.markAsDirty(); control.markAsDirty();
}); });
} }
} }

View File

@ -1,82 +1,82 @@
<app-navbar title="Карта гостя" (backEvent)="logout()"></app-navbar> <app-navbar title="Карта гостя" (backEvent)="logout()"></app-navbar>
<div class="guest-card"> <div class="guest-card">
<div class="top-info"> <div class="top-info">
<div class="top-info__level"> <div class="top-info__level">
<p id="level">Уровень {{ currentLvl }}</p> <p id="level">Уровень {{ currentLvl }}</p>
<p id="level-percent">Кэшбек {{ currentLvlPeriod.percent }}%</p> <p id="level-percent">Кэшбек {{ currentLvlPeriod.percent }}%</p>
</div> </div>
<p class="top-info__bonus">{{ getBalanceAmount(customerInfo?.walletBalances) }} бонусов</p> <p class="top-info__bonus">{{ getBalanceAmount(customerInfo?.walletBalances) }} бонусов</p>
</div> </div>
<div class="guest-card__qr" (click)="qrCodeClick()"> <div class="guest-card__qr" (click)="qrCodeClick()">
<ng-container *ngIf="customerInfo"> <ng-container *ngIf="customerInfo">
<qr-code <qr-code
[value]="customerInfo?.phone.substr(2) || 'Данные не найдены'" [value]="customerInfo?.phone.substr(2) || 'Данные не найдены'"
[margin]="0" [margin]="0"
[size]="qrCodeSize" [size]="qrCodeSize"
errorCorrectionLevel="M" errorCorrectionLevel="M"
></qr-code> ></qr-code>
</ng-container> </ng-container>
<ng-container *ngIf="!customerInfo"> <ng-container *ngIf="!customerInfo">
<ng-container <ng-container
*ngTemplateOutlet="spinner; context: { $implicit: 85 }" *ngTemplateOutlet="spinner; context: { $implicit: 85 }"
></ng-container> ></ng-container>
</ng-container> </ng-container>
</div> </div>
<div class="guest-card__user-description"> <div class="guest-card__user-description">
<p> <p>
<ng-container *ngIf="currentLvlPeriod.end"> <ng-container *ngIf="currentLvlPeriod.end">
Осталось купить на сумму <span class="price">{{ currentLvlPeriod.end - (purchaseData.currentAmount || 0) + 1 }}</span> Осталось купить на сумму <span class="price">{{ currentLvlPeriod.end - (purchaseData.currentAmount || 0) + 1 }}</span>
рублей, тогда кэшбек будет <span class="percent">{{ getNextLevel().percent }}%</span> с рублей, тогда кэшбек будет <span class="percent">{{ getNextLevel().percent }}%</span> с
{{ purchaseData.currentPeriod[1].locale('ru').format('D MMMM')}} {{ purchaseData.currentPeriod[1].locale('ru').format('D MMMM')}}
</ng-container> </ng-container>
<ng-container *ngIf="!currentLvlPeriod.end"> <ng-container *ngIf="!currentLvlPeriod.end">
У Вас последний уровень бонусной программы. Процент начисляемых бонусов: У Вас последний уровень бонусной программы. Процент начисляемых бонусов:
{{currentLvlPeriod.percent}}% {{currentLvlPeriod.percent}}%
</ng-container> </ng-container>
</p> </p>
</div> </div>
<span id="bonuses-condition"></span> <span id="bonuses-condition"></span>
<!-- <div class="guest-card__level-info"> --> <!-- <div class="guest-card__level-info"> -->
<!-- <ng-container *ngIf="!purchaseData.$loading"> --> <!-- <ng-container *ngIf="!purchaseData.$loading"> -->
<!-- <ng-container *ngIf="currentLvlPeriod.end"> --> <!-- <ng-container *ngIf="currentLvlPeriod.end"> -->
<!-- <\!-- <input -\-> --> <!-- <\!-- <input -\-> -->
<!-- <\!-- type="range" -\-> --> <!-- <\!-- type="range" -\-> -->
<!-- <\!-- [value]="purchaseData.currentAmount" -\-> --> <!-- <\!-- [value]="purchaseData.currentAmount" -\-> -->
<!-- <\!-- [min]="currentLvlPeriod.start" -\-> --> <!-- <\!-- [min]="currentLvlPeriod.start" -\-> -->
<!-- <\!-- [max]="currentLvlPeriod.end" -\-> --> <!-- <\!-- [max]="currentLvlPeriod.end" -\-> -->
<!-- <\!-- [step]="0.01" -\-> --> <!-- <\!-- [step]="0.01" -\-> -->
<!-- <\!-- disabled="true" -\-> --> <!-- <\!-- disabled="true" -\-> -->
<!-- <\!-- [ngStyle]="{ -\-> --> <!-- <\!-- [ngStyle]="{ -\-> -->
<!-- <\!-- 'background-size': ((purchaseData.currentAmount! - currentLvlPeriod.start) / (currentLvlPeriod.end - currentLvlPeriod.start + 1)) * 100 + '% 100%' -\-> --> <!-- <\!-- 'background-size': ((purchaseData.currentAmount! - currentLvlPeriod.start) / (currentLvlPeriod.end - currentLvlPeriod.start + 1)) * 100 + '% 100%' -\-> -->
<!-- <\!-- }" -\-> --> <!-- <\!-- }" -\-> -->
<!-- <\!-- /> -\-> --> <!-- <\!-- /> -\-> -->
<!-- </ng-container> --> <!-- </ng-container> -->
<!-- <ng-container *ngIf="!currentLvlPeriod.end"> --> <!-- <ng-container *ngIf="!currentLvlPeriod.end"> -->
<!-- <h2> --> <!-- <h2> -->
<!-- У Вас последний уровень бонусной программы. Процент начисляемых бонусов: {{currentLvlPeriod.percent}}% --> <!-- У Вас последний уровень бонусной программы. Процент начисляемых бонусов: {{currentLvlPeriod.percent}}% -->
<!-- </h2> --> <!-- </h2> -->
<!-- </ng-container> --> <!-- </ng-container> -->
<!-- </ng-container> --> <!-- </ng-container> -->
<!-- <ng-container *ngIf="purchaseData.$loading"> --> <!-- <ng-container *ngIf="purchaseData.$loading"> -->
<!-- <ng-container --> <!-- <ng-container -->
<!-- *ngTemplateOutlet="spinner; context: { $implicit: 48 }" --> <!-- *ngTemplateOutlet="spinner; context: { $implicit: 48 }" -->
<!-- ></ng-container> --> <!-- ></ng-container> -->
<!-- </ng-container> --> <!-- </ng-container> -->
<!-- </div> --> <!-- </div> -->
<app-last-order <app-last-order
[lastOrder]="lastPurchase" [lastOrder]="lastPurchase"
[loading]="purchaseData.$loading" [loading]="purchaseData.$loading"
></app-last-order> ></app-last-order>
<a class="guest-card__loyalty-program" routerLink="loyality-program" <a class="guest-card__loyalty-program" routerLink="loyality-program"
>Подробнее о правилах <br /> >Подробнее о правилах <br />
Программы лояльности</a Программы лояльности</a
> >
</div> </div>
<ng-template #spinner let-diameter> <ng-template #spinner let-diameter>
<mat-spinner [strokeWidth]="3" [diameter]="diameter"></mat-spinner> <mat-spinner [strokeWidth]="3" [diameter]="diameter"></mat-spinner>
</ng-template> </ng-template>

View File

@ -1,222 +1,222 @@
:host { :host {
.guest-card { .guest-card {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
max-width: 600px; max-width: 600px;
margin: 0 auto; margin: 0 auto;
.top-info { .top-info {
padding: 8px 0 0; padding: 8px 0 0;
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
background-color: #292929; background-color: #292929;
border-bottom-left-radius: 10px; border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px; border-bottom-right-radius: 10px;
border: 1px solid #000000; border: 1px solid #000000;
border-top: 1px solid #888888; border-top: 1px solid #888888;
&__level { &__level {
width: 100%; width: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-around; justify-content: space-around;
font-size: 16px; font-size: 16px;
} }
& p { & p {
color: var(--button-color); color: var(--button-color);
} }
p.top-info__bonus{ p.top-info__bonus{
font-size: 20px; font-size: 20px;
} }
} }
&__qr { &__qr {
margin: 10px; margin: 10px;
padding: 10px; padding: 10px;
width: fit-content; width: fit-content;
background-image: linear-gradient( background-image: linear-gradient(
#fff 33%, #fff 33%,
transparent 0px, transparent 0px,
transparent 67%, transparent 67%,
#fff 0px #fff 0px
), ),
linear-gradient( linear-gradient(
90deg, 90deg,
#ffe 33%, #ffe 33%,
transparent 0px, transparent 0px,
transparent 66%, transparent 66%,
#fff 0px #fff 0px
), ),
linear-gradient(#fff 33%, transparent 0px, transparent 67%, #fff 0), linear-gradient(#fff 33%, transparent 0px, transparent 67%, #fff 0),
linear-gradient(90deg, #fff 33%, transparent 0, transparent 66%, #fff 0); linear-gradient(90deg, #fff 33%, transparent 0, transparent 66%, #fff 0);
background-size: 1px 100%, 100% 1px, 1px 100%, 100% 1px; background-size: 1px 100%, 100% 1px, 1px 100%, 100% 1px;
background-position: 0 0, 0 0, 100% 100%, 100% 100%; background-position: 0 0, 0 0, 100% 100%, 100% 100%;
background-repeat: no-repeat, no-repeat, no-repeat, no-repeat; background-repeat: no-repeat, no-repeat, no-repeat, no-repeat;
cursor: pointer; cursor: pointer;
} }
&__user-description { &__user-description {
background-color: #292929; background-color: #292929;
padding: 14px 24px; padding: 14px 24px;
p { p {
width: 60%; width: 60%;
text-align: center; text-align: center;
margin: 0 auto; margin: 0 auto;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 16px; font-size: 16px;
line-height: 19px; line-height: 19px;
letter-spacing: -0.5px; letter-spacing: -0.5px;
color: var(--text-color); color: var(--text-color);
} }
.price, .price,
.percent { .percent {
font-weight: bold; font-weight: bold;
} }
} }
&__purchases-description { &__purchases-description {
margin: 0; margin: 0;
padding: 14px 24px; padding: 14px 24px;
width: 100%; width: 100%;
text-align: center; text-align: center;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 16px; font-size: 16px;
line-height: 19px; line-height: 19px;
letter-spacing: -0.5px; letter-spacing: -0.5px;
grid-template-columns: calc(100% - 24px) 24px; grid-template-columns: calc(100% - 24px) 24px;
span { span {
color: #219653; color: #219653;
} }
} }
&__level-info { &__level-info {
padding-top: 36px; padding-top: 36px;
padding-left: 36px; padding-left: 36px;
padding-right: 36px; padding-right: 36px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
h2 { h2 {
font-style: normal; font-style: normal;
font-family: Montserrat; font-family: Montserrat;
font-weight: 700; font-weight: 700;
font-size: 17px; font-size: 17px;
line-height: 22px; line-height: 22px;
text-align: center; text-align: center;
letter-spacing: -0.408px; letter-spacing: -0.408px;
} }
input[type="range"] { input[type="range"] {
-webkit-appearance: none; -webkit-appearance: none;
width: 100%; width: 100%;
height: 6px; height: 6px;
border-radius: 5px; border-radius: 5px;
display: block; display: block;
position: relative; position: relative;
background: #231f20; background: #231f20;
box-shadow: 0px 0px 3px #f2c94c59; box-shadow: 0px 0px 3px #f2c94c59;
background-image: linear-gradient(#f2c94c, #f2c94c); background-image: linear-gradient(#f2c94c, #f2c94c);
background-size: 70% 100%; background-size: 70% 100%;
background-repeat: no-repeat; background-repeat: no-repeat;
&::before, &::before,
&::after { &::after {
content: " "; content: " ";
display: block; display: block;
width: 16px; width: 16px;
height: 16px; height: 16px;
border-radius: 100%; border-radius: 100%;
position: absolute; position: absolute;
top: -5px; top: -5px;
} }
&::before { &::before {
background-color: #f2c94c; background-color: #f2c94c;
left: 0px; left: 0px;
} }
&::after { &::after {
background-color: #f2c94c; background-color: #f2c94c;
right: 0px; right: 0px;
} }
&::-webkit-slider-thumb { &::-webkit-slider-thumb {
-webkit-appearance: none; -webkit-appearance: none;
background: #f2c94c; background: #f2c94c;
width: 16px; width: 16px;
height: 16px; height: 16px;
border-radius: 100%; border-radius: 100%;
display: none; display: none;
} }
&::-ms-thumb { &::-ms-thumb {
-webkit-appearance: none; -webkit-appearance: none;
height: 20px; height: 20px;
width: 20px; width: 20px;
border-radius: 50%; border-radius: 50%;
background: #f2c94c; background: #f2c94c;
cursor: ew-resize; cursor: ew-resize;
box-shadow: 0 0 2px 0 #555; box-shadow: 0 0 2px 0 #555;
transition: background 0.3s ease-in-out; transition: background 0.3s ease-in-out;
} }
&::-moz-range-thumb { &::-moz-range-thumb {
-webkit-appearance: none; -webkit-appearance: none;
height: 20px; height: 20px;
width: 20px; width: 20px;
border-radius: 50%; border-radius: 50%;
background: #f2c94c; background: #f2c94c;
cursor: ew-resize; cursor: ew-resize;
box-shadow: 0 0 2px 0 #555; box-shadow: 0 0 2px 0 #555;
transition: background 0.3s ease-in-out; transition: background 0.3s ease-in-out;
} }
} }
} }
&__download-app { &__download-app {
width: 100%; width: 100%;
position: relative; position: relative;
margin-top: 32px; margin-top: 32px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
img { img {
width: 100%; width: 100%;
max-width: calc(100% - 16px); max-width: calc(100% - 16px);
} }
} }
&__loyalty-program { &__loyalty-program {
text-align: center; text-align: center;
color: #7F2061; color: #7F2061;
font-style: normal; font-style: normal;
font-weight: bold; font-weight: bold;
font-size: 12px; font-size: 12px;
line-height: 16px; line-height: 16px;
text-decoration: none; text-decoration: none;
margin: 17px 0 22px; margin: 17px 0 22px;
} }
} }
} }
app-accordion { app-accordion {
ul { ul {
li { li {
list-style-type: disc; list-style-type: disc;
margin-left: 16px; margin-left: 16px;
} }
} }
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GuestCardComponent } from './guest-card.component'; import { GuestCardComponent } from './guest-card.component';
describe('GuestCardComponent', () => { describe('GuestCardComponent', () => {
let component: GuestCardComponent; let component: GuestCardComponent;
let fixture: ComponentFixture<GuestCardComponent>; let fixture: ComponentFixture<GuestCardComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ GuestCardComponent ] declarations: [ GuestCardComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(GuestCardComponent); fixture = TestBed.createComponent(GuestCardComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,193 +1,193 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { MatBottomSheet } from '@angular/material/bottom-sheet'; import { MatBottomSheet } from '@angular/material/bottom-sheet';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { ExitComponent } from 'src/app/components/exit/exit.component'; import { ExitComponent } from 'src/app/components/exit/exit.component';
import { CookiesService } from 'src/app/services/cookies.service'; import { CookiesService } from 'src/app/services/cookies.service';
import { WpJsonService } from 'src/app/services/wp-json.service'; import { WpJsonService } from 'src/app/services/wp-json.service';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
import moment from 'moment'; import moment from 'moment';
import { Purchase, lvlPeriod } from 'src/app/interface/data'; import { Purchase, lvlPeriod } from 'src/app/interface/data';
import { lvlPeriods } from 'src/app/app.constants'; import { lvlPeriods } from 'src/app/app.constants';
interface Moment extends moment.Moment {} interface Moment extends moment.Moment {}
export interface IPurchaseData { export interface IPurchaseData {
currentPeriod: Moment[]; currentPeriod: Moment[];
lastPeriod: Moment[]; lastPeriod: Moment[];
lastPurchases: Purchase[]; lastPurchases: Purchase[];
currentPurchases: Purchase[]; currentPurchases: Purchase[];
lastAmount?: number; lastAmount?: number;
currentAmount?: number; currentAmount?: number;
$loading: boolean; $loading: boolean;
} }
@Component({ @Component({
selector: 'app-guest-card', selector: 'app-guest-card',
templateUrl: './guest-card.component.html', templateUrl: './guest-card.component.html',
styleUrls: ['./guest-card.component.scss'], styleUrls: ['./guest-card.component.scss'],
}) })
export class GuestCardComponent implements OnInit { export class GuestCardComponent implements OnInit {
public qrCodeSize: number = 85; public qrCodeSize: number = 85;
private isQrCodeClicked: boolean = false; private isQrCodeClicked: boolean = false;
public customerInfo!: any; public customerInfo!: any;
public purchases!: Purchase[]; public purchases!: Purchase[];
public lastPurchase!: Purchase; public lastPurchase!: Purchase;
public purchaseData: IPurchaseData = { public purchaseData: IPurchaseData = {
currentPeriod: [], currentPeriod: [],
lastPeriod: [], lastPeriod: [],
lastPurchases: [], lastPurchases: [],
currentPurchases: [], currentPurchases: [],
$loading: true, $loading: true,
get currentAmount():number { get currentAmount():number {
const amount = this.currentPurchases.reduce((accumulator, currentValue) => { const amount = this.currentPurchases.reduce((accumulator, currentValue) => {
if (currentValue.transactionType !== 'PayFromWallet') return accumulator; if (currentValue.transactionType !== 'PayFromWallet') return accumulator;
return accumulator + currentValue.orderSum!; return accumulator + currentValue.orderSum!;
}, 0); }, 0);
return amount * 1 return amount * 1
}, },
get lastAmount():number { get lastAmount():number {
const amount = this.lastPurchases.reduce((accumulator, currentValue) => { const amount = this.lastPurchases.reduce((accumulator, currentValue) => {
if (currentValue.transactionType !== 'PayFromWallet') return accumulator; if (currentValue.transactionType !== 'PayFromWallet') return accumulator;
return accumulator + currentValue.orderSum!; return accumulator + currentValue.orderSum!;
}, 0); }, 0);
return amount * 1 return amount * 1
} }
}; };
public discountLevel: number = 4.2; public discountLevel: number = 4.2;
public lvlPeriods: lvlPeriod[] = lvlPeriods; public lvlPeriods: lvlPeriod[] = lvlPeriods;
public currentLvlPeriod!: lvlPeriod; public currentLvlPeriod!: lvlPeriod;
public currentLvl: number = 1; public currentLvl: number = 1;
constructor( constructor(
private _bottomSheet: MatBottomSheet, private _bottomSheet: MatBottomSheet,
private cookiesService: CookiesService, private cookiesService: CookiesService,
private router: Router, private router: Router,
private wpJsonService: WpJsonService private wpJsonService: WpJsonService
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
const token = this.cookiesService.getItem('token'); const token = this.cookiesService.getItem('token');
this.getCurrentQuarterOfYear(); this.getCurrentQuarterOfYear();
this.wpJsonService this.wpJsonService
.getCustomerInfo( .getCustomerInfo(
environment.systemId, environment.systemId,
token || '', token || '',
environment.icardProxy environment.icardProxy
) )
.subscribe({ .subscribe({
next: (value) => { next: (value) => {
this.customerInfo = value.customer_info; this.customerInfo = value.customer_info;
this.getPurchases().subscribe((value) => { this.getPurchases().subscribe((value) => {
this.purchases = value[this.customerInfo?.id].filter( this.purchases = value[this.customerInfo?.id].filter(
(purchase: Purchase) => (purchase: Purchase) =>
[ [
'PayFromWallet', 'PayFromWallet',
'CancelPayFromWallet', 'CancelPayFromWallet',
'RefillWallet', 'RefillWallet',
'RefillWalletFromOrder' 'RefillWalletFromOrder'
].includes(purchase.transactionType || '') ].includes(purchase.transactionType || '')
); );
this.lastPurchase = this.purchases.filter( this.lastPurchase = this.purchases.filter(
(purchase: Purchase) => (purchase: Purchase) =>
[ [
'PayFromWallet', 'PayFromWallet',
'RefillWalletFromOrder' 'RefillWalletFromOrder'
].includes(purchase.transactionType || '') ].includes(purchase.transactionType || '')
)[0] )[0]
this.purchaseData.lastPurchases = this.purchases.filter((value: Purchase) => { this.purchaseData.lastPurchases = this.purchases.filter((value: Purchase) => {
return moment(value.transactionCreateDate).isBetween(this.purchaseData.lastPeriod[0], this.purchaseData.lastPeriod[1]) return moment(value.transactionCreateDate).isBetween(this.purchaseData.lastPeriod[0], this.purchaseData.lastPeriod[1])
}) })
this.purchaseData.currentPurchases = this.purchases.filter((value: Purchase) => { this.purchaseData.currentPurchases = this.purchases.filter((value: Purchase) => {
return moment(value.transactionCreateDate).isBetween(this.purchaseData.currentPeriod[0], this.purchaseData.currentPeriod[1]) return moment(value.transactionCreateDate).isBetween(this.purchaseData.currentPeriod[0], this.purchaseData.currentPeriod[1])
}) })
const currentAmount = this.purchaseData.currentAmount || 0 const currentAmount = this.purchaseData.currentAmount || 0
const index = this.lvlPeriods.findIndex((item) => item.start <= currentAmount && currentAmount <= (item.end || Infinity))! const index = this.lvlPeriods.findIndex((item) => item.start <= currentAmount && currentAmount <= (item.end || Infinity))!
if(index != -1) { if(index != -1) {
this.currentLvlPeriod = this.lvlPeriods[index]; this.currentLvlPeriod = this.lvlPeriods[index];
this.currentLvl = index + 1; this.currentLvl = index + 1;
this.purchaseData.$loading = false; this.purchaseData.$loading = false;
} }
}); });
}, },
}); });
} }
getNextLevel(): lvlPeriod { getNextLevel(): lvlPeriod {
if(this.currentLvl == this.lvlPeriods.length) { if(this.currentLvl == this.lvlPeriods.length) {
return lvlPeriods[this.currentLvl - 1]; return lvlPeriods[this.currentLvl - 1];
} }
return lvlPeriods[this.currentLvl]; return lvlPeriods[this.currentLvl];
} }
qrCodeClick() { qrCodeClick() {
this.isQrCodeClicked = !this.isQrCodeClicked; this.isQrCodeClicked = !this.isQrCodeClicked;
this.qrCodeSize = this.isQrCodeClicked ? 180 : 85; this.qrCodeSize = this.isQrCodeClicked ? 180 : 85;
} }
deleteToken(): void { deleteToken(): void {
this.cookiesService.logout(); this.cookiesService.logout();
} }
logout() { logout() {
const bottomSheet = this._bottomSheet.open(ExitComponent); const bottomSheet = this._bottomSheet.open(ExitComponent);
bottomSheet.afterDismissed().subscribe({ bottomSheet.afterDismissed().subscribe({
next: (val) => { next: (val) => {
if (val) { if (val) {
this.deleteToken(); this.deleteToken();
this.router.navigate(['/login']); this.router.navigate(['/login']);
} }
}, },
}); });
} }
getPurchases( getPurchases(
start: Date | Moment = moment().subtract(1, 'months').startOf('month'), start: Date | Moment = moment().subtract(1, 'months').startOf('month'),
end: Date | Moment = moment() end: Date | Moment = moment()
): Observable<any> { ): Observable<any> {
const token = this.cookiesService.getItem('token'); const token = this.cookiesService.getItem('token');
const delta = moment(end).diff(moment(start), 'days'); const delta = moment(end).diff(moment(start), 'days');
return this.wpJsonService.getTransactions( return this.wpJsonService.getTransactions(
environment.systemId, environment.systemId,
token ?? '', token ?? '',
environment.icardProxy environment.icardProxy
); );
} }
getCurrentQuarterOfYear() { getCurrentQuarterOfYear() {
const quarters = [ const quarters = [
[ [
moment().subtract(1, 'years').endOf('year').subtract(3, 'months'), moment().subtract(1, 'years').endOf('year').subtract(3, 'months'),
moment().subtract(1, 'years').endOf('year'), moment().subtract(1, 'years').endOf('year'),
], ],
[moment().startOf('year'), moment().startOf('year').add(3, 'months')], [moment().startOf('year'), moment().startOf('year').add(3, 'months')],
[ [
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(6, 'months'), moment().startOf('year').add(6, 'months'),
moment().startOf('year').add(9, 'months'), moment().startOf('year').add(9, 'months'),
], ],
[ [
moment().startOf('year').add(9, 'months'), moment().startOf('year').add(9, 'months'),
moment().startOf('year').add(12, 'months'), moment().startOf('year').add(12, 'months'),
], ],
]; ];
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
if (moment().isBetween(quarters[i][0], quarters[i][1])) { if (moment().isBetween(quarters[i][0], quarters[i][1])) {
this.purchaseData.lastPeriod = quarters[i - 1]; this.purchaseData.lastPeriod = quarters[i - 1];
this.purchaseData.currentPeriod = quarters[i]; this.purchaseData.currentPeriod = quarters[i];
} }
} }
} }
getBalanceAmount(loyaltyPrograms: any[]) { getBalanceAmount(loyaltyPrograms: any[]) {
return loyaltyPrograms.reduce((accumulator, currentValue) => { return loyaltyPrograms.reduce((accumulator, currentValue) => {
return accumulator + currentValue.balance; return accumulator + currentValue.balance;
}, 0); }, 0);
} }
} }

View File

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

View File

@ -1,214 +1,214 @@
:host { :host {
padding-top: 48px; padding-top: 48px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
max-width: 600px; max-width: 600px;
margin: 0 auto 52px; margin: 0 auto 52px;
h1 { h1 {
margin-top: 20px; margin-top: 20px;
width: 302px; width: 302px;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 17px; font-size: 17px;
line-height: 22px; line-height: 22px;
text-align: center; text-align: center;
letter-spacing: -0.408px; letter-spacing: -0.408px;
} }
h2 { h2 {
margin-top: 44px; margin-top: 44px;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 20px; font-size: 20px;
line-height: 24px; line-height: 24px;
text-align: center; text-align: center;
letter-spacing: 0.38px; letter-spacing: 0.38px;
} }
.description { .description {
width: 180px; width: 180px;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 15px; font-size: 15px;
line-height: 20px; line-height: 20px;
text-align: center; text-align: center;
letter-spacing: -0.24px; letter-spacing: -0.24px;
margin-top: 16px; margin-top: 16px;
} }
form { form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin-top: 35px; margin-top: 35px;
.offer { .offer {
margin-top: 10px; margin-top: 10px;
padding: 0 16px; padding: 0 16px;
font-family: "Gowun Dodum"; font-family: "Gowun Dodum";
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 17px; line-height: 17px;
text-align: center; text-align: center;
span { span {
color: #13a538; color: #13a538;
} }
} }
.input-container { .input-container {
position: relative; position: relative;
width: 100%; width: 100%;
label { label {
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 16px; line-height: 16px;
color: #6a737c; color: #6a737c;
text-align: left; text-align: left;
position: absolute; position: absolute;
top: 10px; top: 10px;
left: 16px; left: 16px;
} }
input { input {
width: 100%; width: 100%;
padding: 24px 16px 8px; padding: 24px 16px 8px;
background-color: #252323; background-color: #252323;
margin-bottom: 16px; margin-bottom: 16px;
border: none; border: none;
border-top: solid #6a737c 1px; border-top: solid #6a737c 1px;
border-bottom: solid #6a737c 1px; border-bottom: solid #6a737c 1px;
color: #6a737c; color: #6a737c;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 22px; font-size: 22px;
line-height: 28px; line-height: 28px;
} }
} }
button { button {
height: 46px; height: 46px;
width: calc(100% - 32px); width: calc(100% - 32px);
padding: 10px 24px; padding: 10px 24px;
background: #28af49; background: #28af49;
border-radius: 6px; border-radius: 6px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
border: none; border: none;
letter-spacing: -0.408px; letter-spacing: -0.408px;
color: #ffffff; color: #ffffff;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 17px; font-size: 17px;
line-height: 22px; line-height: 22px;
&:disabled { &:disabled {
background-color: #344a3a; background-color: #344a3a;
} }
} }
} }
.code-form { .code-form {
width: 100%; width: 100%;
// input { // input {
// width: 100%; // width: 100%;
// padding: 24px 16px 8px; // padding: 24px 16px 8px;
// background-color: #252323; // background-color: #252323;
// margin-bottom: 16px; // margin-bottom: 16px;
// border: none; // border: none;
// border-top: solid #6a737c 1px; // border-top: solid #6a737c 1px;
// border-bottom: solid #6a737c 1px; // border-bottom: solid #6a737c 1px;
// color: #6a737c; // color: #6a737c;
// font-style: normal; // font-style: normal;
// font-weight: 400; // font-weight: 400;
// font-size: 22px; // font-size: 22px;
// line-height: 28px; // line-height: 28px;
// } // }
.inputs-container { .inputs-container {
width: 102px; width: 102px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
margin-bottom: 40px; margin-bottom: 40px;
// code // code
.box { .box {
position: relative; position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 42px; height: 42px;
width: 42px; width: 42px;
border-radius: 6px; border-radius: 6px;
// box-shadow: 0 0 6px 1px hsla(240, 54%, 61%, 0.2); // box-shadow: 0 0 6px 1px hsla(240, 54%, 61%, 0.2);
overflow: hidden; overflow: hidden;
will-change: transform; will-change: transform;
} }
.box:focus-within { .box:focus-within {
box-shadow: 0 0 6px 1px rgba($color: #28af49, $alpha: 0.2), box-shadow: 0 0 6px 1px rgba($color: #28af49, $alpha: 0.2),
0 0 0 2px rgba($color: #28af49, $alpha: 0.6); 0 0 0 2px rgba($color: #28af49, $alpha: 0.6);
} }
.box::before, .box::before,
.box::after { .box::after {
content: ""; content: "";
position: absolute; position: absolute;
height: 100%; height: 100%;
width: 100%; width: 100%;
top: 0; top: 0;
left: 0; left: 0;
border-radius: 6px; border-radius: 6px;
overflow: hidden; overflow: hidden;
} }
.box::before { .box::before {
// background: hsl(240, 54%, 97%); // background: hsl(240, 54%, 97%);
z-index: 1; z-index: 1;
transition: background-color 450ms cubic-bezier(0.25, 0.01, 0.25, 1); transition: background-color 450ms cubic-bezier(0.25, 0.01, 0.25, 1);
} }
.box::after { .box::after {
transform: translateY(100%); transform: translateY(100%);
background-color: hsl(145, 0%, 42%); background-color: hsl(145, 0%, 42%);
opacity: 0; opacity: 0;
z-index: 10; z-index: 10;
transition: transform 450ms cubic-bezier(0.25, 0.01, 0.25, 1), transition: transform 450ms cubic-bezier(0.25, 0.01, 0.25, 1),
opacity 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); background-color 450ms cubic-bezier(0.25, 0.01, 0.25, 1);
} }
.field { .field {
position: relative; position: relative;
border: 0; border: 0;
outline: 0; outline: 0;
font-size: 25.21px; font-size: 25.21px;
line-height: 42px; line-height: 42px;
color: #fff; color: #fff;
background-color: transparent; background-color: transparent;
text-align: center; text-align: center;
z-index: 100; z-index: 100;
} }
.field::placeholder { .field::placeholder {
color: #fff; color: #fff;
} }
} }
} }
.resend-code { .resend-code {
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 16px; line-height: 16px;
text-align: center; text-align: center;
margin-top: 23px; margin-top: 23px;
.resend { .resend {
cursor: pointer; cursor: pointer;
} }
} }
} }
mat-form-field { mat-form-field {
width: 100%; width: 100%;
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component'; import { LoginComponent } from './login.component';
describe('LoginComponent', () => { describe('LoginComponent', () => {
let component: LoginComponent; let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>; let fixture: ComponentFixture<LoginComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ LoginComponent ] declarations: [ LoginComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(LoginComponent); fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

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

View File

@ -1,99 +1,99 @@
<app-navbar title="Программа лояльности" (backEvent)="goBack()"></app-navbar> <app-navbar title="Программа лояльности" (backEvent)="goBack()"></app-navbar>
<div class="loyality-program"> <div class="loyality-program">
<app-accordion header="Условия начисления бонусов"> <app-accordion header="Условия начисления бонусов">
<p> <p>
Расчет начисления бонусов - 10% от суммы покупок за период с 11.01.2023г. Расчет начисления бонусов - 10% от суммы покупок за период с 11.01.2023г.
по 31.03.2023 г. по 31.03.2023 г.
</p> </p>
<p> <p>
За период с 11.01.2023г. по 31.03.2023 г. сумма ваших покупок составила За период с 11.01.2023г. по 31.03.2023 г. сумма ваших покупок составила
3700 руб. 3700 руб.
</p> </p>
<p>Начисляемый бонус 10% от суммы покупок</p> <p>Начисляемый бонус 10% от суммы покупок</p>
</app-accordion> </app-accordion>
<app-accordion header="Условия «оплаты» покупки бонусами"> <app-accordion header="Условия «оплаты» покупки бонусами">
<p> <p>
Участник может использовать Бонусы для «оплаты» до 100% стоимости любой Участник может использовать Бонусы для «оплаты» до 100% стоимости любой
покупки. покупки.
</p> </p>
<p> <p>
Списание Бонусов происходит из расчета 1:1 (один Бонус дает скидку 1 Списание Бонусов происходит из расчета 1:1 (один Бонус дает скидку 1
российский рубль / 1 тенге / 1 белорусский рубль. Скидка, предоставляемая российский рубль / 1 тенге / 1 белорусский рубль. Скидка, предоставляемая
Участнику при списании Бонусов, уменьшает цену товаров в заказе в Участнику при списании Бонусов, уменьшает цену товаров в заказе в
соответствии с условиями ПЛ. соответствии с условиями ПЛ.
</p> </p>
<p> <p>
Для списания Бонусов Участник должен попросить об этом в кофе-баре сети Для списания Бонусов Участник должен попросить об этом в кофе-баре сети
«COFFEE LIKE» кассира до момента пробития фискального чека, после чего им «COFFEE LIKE» кассира до момента пробития фискального чека, после чего им
будет проверена возможность списания Бонусов. будет проверена возможность списания Бонусов.
</p> </p>
<p> <p>
Для всех Участников возможно списание без использования мобильного Для всех Участников возможно списание без использования мобильного
приложения. приложения.
</p> </p>
<p>Полученные Бонусы не подлежат обмену на денежные средства.</p> <p>Полученные Бонусы не подлежат обмену на денежные средства.</p>
</app-accordion> </app-accordion>
<app-accordion header="Особые условия"> <app-accordion header="Особые условия">
<p> <p>
Начисленные на счет бонусы сгорают по прошествии 90 дней с момента Начисленные на счет бонусы сгорают по прошествии 90 дней с момента
совершения последней покупки с начислением или списанием бонусов. совершения последней покупки с начислением или списанием бонусов.
</p> </p>
<ul> <ul>
Возврат покупки, за которую бонусы были начислены: Возврат покупки, за которую бонусы были начислены:
<li> <li>
В случае, если бонусов на счету достаточно для списания, бонусы В случае, если бонусов на счету достаточно для списания, бонусы
списываются в полном ранее начисленном за возвращаемый товар объеме. списываются в полном ранее начисленном за возвращаемый товар объеме.
</li> </li>
<li> <li>
В случае, если бонусов на счету недостаточно, формируется минусовой В случае, если бонусов на счету недостаточно, формируется минусовой
баланс. баланс.
</li> </li>
</ul> </ul>
<ul> <ul>
Возврат покупки, которая была оплачена бонусами: Возврат покупки, которая была оплачена бонусами:
<li> <li>
В случае предъявления Участником кассового или товарного чека, сумма В случае предъявления Участником кассового или товарного чека, сумма
бонусов, списанная для оплаты возвращаемого товара, зачисляется на счет бонусов, списанная для оплаты возвращаемого товара, зачисляется на счет
участника. участника.
</li> </li>
<li> <li>
В случае возврата товара с применением оплаты бонусами, клиенту В случае возврата товара с применением оплаты бонусами, клиенту
возвращается денежная сумма в размере, внесенном Участником в оплату возвращается денежная сумма в размере, внесенном Участником в оплату
товара при покупке, за вычетом суммы, оплаченной бонусами. товара при покупке, за вычетом суммы, оплаченной бонусами.
</li> </li>
</ul> </ul>
</app-accordion> </app-accordion>
<app-accordion header="Узнать % начисляемых бонусов"> <app-accordion header="Узнать % начисляемых бонусов">
<p> <p>
Начисление Бонусных баллов происходит по дифференцированной шкале в Начисление Бонусных баллов происходит по дифференцированной шкале в
зависимости от уровня: зависимости от уровня:
</p> </p>
<ng-container <ng-container
*ngFor="let item of lvlPeriods; let index = index; let last = last" *ngFor="let item of lvlPeriods; let index = index; let last = last"
> >
<ng-container *ngIf="!last"> <ng-container *ngIf="!last">
<ul> <ul>
<span [style]="{ color: item.color }">Уровень {{ index + 1 }}</span> <span [style]="{ color: item.color }">Уровень {{ index + 1 }}</span>
<li> <li>
Сумма покупок за предыдущий период {{ item.start }}-{{ item.end }} Сумма покупок за предыдущий период {{ item.start }}-{{ item.end }}
руб. руб.
</li> </li>
<li>Начисляемый бонус {{ item.percent }}% от суммы покупки</li> <li>Начисляемый бонус {{ item.percent }}% от суммы покупки</li>
</ul> </ul>
<br /> <br />
</ng-container> </ng-container>
<ng-container *ngIf="last"> <ng-container *ngIf="last">
<ul> <ul>
<span [style]="{ color: item.color }">Уровень {{ index + 1 }}</span> <span [style]="{ color: item.color }">Уровень {{ index + 1 }}</span>
<li>Сумма покупок за предыдущий период — от {{ item.start }} руб.</li> <li>Сумма покупок за предыдущий период — от {{ item.start }} руб.</li>
<li>Начисляемый бонус, в % от суммы покупки - {{ item.percent }}%</li> <li>Начисляемый бонус, в % от суммы покупки - {{ item.percent }}%</li>
</ul> </ul>
</ng-container> </ng-container>
</ng-container> </ng-container>
</app-accordion> </app-accordion>
</div> </div>

View File

@ -1,3 +1,3 @@
.loyality-program { .loyality-program {
margin-bottom: 20px; margin-bottom: 20px;
} }

View File

@ -1,21 +1,21 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { lvlPeriod } from 'src/app/interface/data'; import { lvlPeriod } from 'src/app/interface/data';
import { lvlPeriods } from 'src/app/app.constants'; import { lvlPeriods } from 'src/app/app.constants';
@Component({ @Component({
selector: 'loyality-program', selector: 'loyality-program',
templateUrl: './loyality-program.component.html', templateUrl: './loyality-program.component.html',
styleUrls: ['./loyality-program.component.scss'], styleUrls: ['./loyality-program.component.scss'],
}) })
export class LoyalityProgramComponent implements OnInit { export class LoyalityProgramComponent implements OnInit {
constructor(private _location: Location) {} constructor(private _location: Location) {}
public lvlPeriods: lvlPeriod[] = lvlPeriods; public lvlPeriods: lvlPeriod[] = lvlPeriods;
goBack() { goBack() {
this._location.back(); this._location.back();
} }
ngOnInit(): void {} ngOnInit(): void {}
} }

View File

@ -1,7 +1,7 @@
<div class="main-container"> <div class="main-container">
<!-- <app-navbar></app-navbar> --> <!-- <app-navbar></app-navbar> -->
<p-toast position="top-center"></p-toast> <p-toast position="top-center"></p-toast>
<app-footer-buttons [token]="token" <app-footer-buttons [token]="token"
[isPermissionNotifications]="isPermissionNotifications" [isPermissionNotifications]="isPermissionNotifications"
(requestingPermission)="requestPermission()"></app-footer-buttons> (requestingPermission)="requestPermission()"></app-footer-buttons>
</div> </div>

View File

@ -1,4 +1,4 @@
.main-container { .main-container {
width: 100vw; width: 100vw;
min-height: 100vh; min-height: 100vh;
} }

View File

@ -1,103 +1,103 @@
import { import {
Component, Component,
ComponentRef, ComponentRef,
ElementRef, ElementRef,
EmbeddedViewRef, EmbeddedViewRef,
OnInit, OnInit,
Renderer2, Renderer2,
ViewContainerRef, ViewContainerRef,
} from '@angular/core'; } from '@angular/core';
import { AngularFireMessaging } from '@angular/fire/compat/messaging'; import { AngularFireMessaging } from '@angular/fire/compat/messaging';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { CardComponent } from 'src/app/components/card/card.component'; import { CardComponent } from 'src/app/components/card/card.component';
import { AccountComponent } from '../account/account.component'; import { AccountComponent } from '../account/account.component';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { MessagingService } from 'src/app/services/messaging.service'; import { MessagingService } from 'src/app/services/messaging.service';
import { CookiesService } from 'src/app/services/cookies.service'; import { CookiesService } from 'src/app/services/cookies.service';
@Component({ @Component({
selector: 'app-main', selector: 'app-main',
templateUrl: './main.component.html', templateUrl: './main.component.html',
styleUrls: ['./main.component.scss'], styleUrls: ['./main.component.scss'],
}) })
export class MainComponent implements OnInit { export class MainComponent implements OnInit {
private cardComponent!: ComponentRef<CardComponent>; private cardComponent!: ComponentRef<CardComponent>;
private accountComponent!: ComponentRef<AccountComponent>; private accountComponent!: ComponentRef<AccountComponent>;
public messagingToken!: string | null; public messagingToken!: string | null;
public isPermissionNotifications: boolean = false; public isPermissionNotifications: boolean = false;
public token = ''; public token = '';
public message: any; public message: any;
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private viewContainerRef: ViewContainerRef, private viewContainerRef: ViewContainerRef,
public el: ElementRef, public el: ElementRef,
public renderer: Renderer2, public renderer: Renderer2,
private messagingService: MessagingService, private messagingService: MessagingService,
private messageService: MessageService, private messageService: MessageService,
private cookiesService: CookiesService, private cookiesService: CookiesService,
) { ) {
route.queryParams.subscribe((params) => { route.queryParams.subscribe((params) => {
if (params['token']) { if (params['token']) {
this.token = params['token'] this.token = params['token']
} else { } else {
const token = this.cookiesService.getItem('token') const token = this.cookiesService.getItem('token')
this.token = token ? token : ''; this.token = token ? token : '';
}; };
}); });
} }
ngOnInit(): void { ngOnInit(): void {
this.appendAccount(); this.appendAccount();
// this.checkRequestPermission() // this.checkRequestPermission()
} }
checkRequestPermission() { checkRequestPermission() {
this.isPermissionNotifications = this.isPermissionNotifications =
Notification.permission !== 'granted' ? false : true; Notification.permission !== 'granted' ? false : true;
} }
requestPermission() { requestPermission() {
const userAgent = window.navigator.userAgent.toLowerCase(); const userAgent = window.navigator.userAgent.toLowerCase();
const ios = /iphone|ipod|ipad/.test(userAgent); const ios = /iphone|ipod|ipad/.test(userAgent);
if (ios) { if (ios) {
this.messageService.clear(); this.messageService.clear();
this.messageService.add({ this.messageService.add({
severity: 'custom', severity: 'custom',
summary: `Чтобы получать уведомления, добавьте карту в Apple Wallet`, summary: `Чтобы получать уведомления, добавьте карту в Apple Wallet`,
life: 5000, life: 5000,
}); });
// var permissionData = window.safari.pushNotification.permission('web.com.example.domain'); // var permissionData = window.safari.pushNotification.permission('web.com.example.domain');
// $scope.checkRemotePermission(permissionData); // $scope.checkRemotePermission(permissionData);
} else { } else {
//FIREBASE HERE //FIREBASE HERE
this.messagingService.requestPermission(); this.messagingService.requestPermission();
this.messagingService.receiveMessage(); this.messagingService.receiveMessage();
this.message = this.messagingService.currentMessage; this.message = this.messagingService.currentMessage;
} }
} }
appendCard(): void { appendCard(): void {
const route = this.route.snapshot.url[0]?.path; const route = this.route.snapshot.url[0]?.path;
const element = document.getElementsByClassName('main-container'); const element = document.getElementsByClassName('main-container');
if (!route && element[0]) { if (!route && element[0]) {
this.cardComponent = this.viewContainerRef.createComponent(CardComponent); this.cardComponent = this.viewContainerRef.createComponent(CardComponent);
const domElem = (this.cardComponent.hostView as EmbeddedViewRef<any>) const domElem = (this.cardComponent.hostView as EmbeddedViewRef<any>)
.rootNodes[0] as HTMLElement; .rootNodes[0] as HTMLElement;
element[0].appendChild(domElem); element[0].appendChild(domElem);
} }
} }
appendAccount(): void { appendAccount(): void {
const route = this.route.snapshot.url[0]?.path; const route = this.route.snapshot.url[0]?.path;
const element = document.getElementsByClassName('main-container'); const element = document.getElementsByClassName('main-container');
if (element[0]) { if (element[0]) {
this.accountComponent = this.accountComponent =
this.viewContainerRef.createComponent(AccountComponent); this.viewContainerRef.createComponent(AccountComponent);
const domElem = (this.accountComponent.hostView as EmbeddedViewRef<any>) const domElem = (this.accountComponent.hostView as EmbeddedViewRef<any>)
.rootNodes[0] as HTMLElement; .rootNodes[0] as HTMLElement;
element[0].appendChild(domElem); element[0].appendChild(domElem);
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show More