Compare commits
165 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea66abfcd1 | ||
| 7c48e1dc1e | |||
| feeed2c640 | |||
| 46059461db | |||
|
|
14c2af863b | ||
|
|
cc192a6b0f | ||
|
|
1b8cb2b185 | ||
|
|
eb15e47328 | ||
|
|
73de9ea6b7 | ||
|
|
c1219e1637 | ||
|
|
9fd031f423 | ||
|
|
7fe4b6de52 | ||
|
|
55c98f5aa1 | ||
|
|
75ddd847f3 | ||
|
|
e5c2e0efe4 | ||
|
|
54795be75a | ||
|
|
0e5e7e1d84 | ||
|
|
4c7b5437df | ||
|
|
e640539d96 | ||
|
|
7808270bb4 | ||
|
|
e12eb7a542 | ||
|
|
0abdb264f5 | ||
|
|
06f668cd3d | ||
| b6483f78a7 | |||
| 9b3488775c | |||
|
|
e5ae004a29 | ||
|
|
142ece15de | ||
|
|
c9bcf3fc15 | ||
|
|
9e7394e848 | ||
|
|
8ea4a41786 | ||
|
|
7fa882e423 | ||
|
|
03771d2012 | ||
|
|
3ff34525ed | ||
|
|
4e0a41b644 | ||
|
|
e20cd30a54 | ||
|
|
abdb5b790c | ||
|
|
10e180613f | ||
|
|
6d55ca2b14 | ||
|
|
fcdb3d8fc7 | ||
|
|
0ae4e31cfd | ||
|
|
50ae2b601e | ||
|
|
798ec5e21a | ||
|
|
15e9fe7ec4 | ||
|
|
26b2e8eb13 | ||
|
|
7b5200d9f8 | ||
|
|
d66f72dc2e | ||
|
|
0ca219c7fa | ||
|
|
1a6345431d | ||
|
|
bef4ed1a48 | ||
|
|
eef97fd35f | ||
|
|
a8062667df | ||
|
|
46d10e50ea | ||
|
|
ccaa41ddf9 | ||
|
|
0e38321526 | ||
|
|
4e52e180d8 | ||
|
|
30854f4cb9 | ||
|
|
425ef99af2 | ||
|
|
b29f2e1fdf | ||
|
|
5c3e4a0368 | ||
|
|
077b60fc87 | ||
|
|
e06d703b69 | ||
|
|
cfe370e3e3 | ||
|
|
e3fd36b881 | ||
| 1b1dcd9b50 | |||
| bb7e39f885 | |||
|
|
b31bd30161 | ||
|
|
7d098d1798 | ||
|
|
7bade7caea | ||
| 238b70e95b | |||
| 877cf018af | |||
|
|
24edd456db | ||
|
|
cff512c561 | ||
|
|
6b60c4d8c4 | ||
| 04884bd0d9 | |||
| 6c283812e7 | |||
|
|
c5e4f3c229 | ||
|
|
2fa05b391e | ||
|
|
ff9e04ecbd | ||
|
|
28ab33248d | ||
|
|
659e9d50bf | ||
|
|
43980516ff | ||
|
|
ca0fa2bfa6 | ||
|
|
b81ee7f713 | ||
|
|
044144735d | ||
|
|
6feeec80f2 | ||
| e062f05f36 | |||
| bf3fd7e0a4 | |||
| 37ad910e75 | |||
| 4a492f16e8 | |||
| 1e7175e3b5 | |||
| be7c62a12b | |||
| 6a075ec773 | |||
| 86ac20f5e5 | |||
|
|
2131e052c6 | ||
|
|
787bc65691 | ||
| 04ecbb837c | |||
| 77ecb32411 | |||
| 9e1cad31f3 | |||
| 7c3a4168c4 | |||
| 76f1799b22 | |||
| 42b46bc478 | |||
| af60b55e94 | |||
| 1da7556aed | |||
| f460954bc8 | |||
| 7a9f9fc872 | |||
| 7bb05324a9 | |||
| 5e3b211778 | |||
| e75999a3c1 | |||
| f8f500f612 | |||
| 72808c3c7c | |||
| efb8095e2f | |||
| 699db43b39 | |||
| 5152d9ad30 | |||
| da340475e0 | |||
| ef8ac5d24d | |||
| fa5d29e517 | |||
| 1a5fa44abc | |||
|
|
bedda578c8 | ||
|
|
44cf8d5074 | ||
|
|
ecdd4f103e | ||
| c0387d967d | |||
| b33ed15666 | |||
| c7ade7515c | |||
| e4bb1d8947 | |||
| c71e2d4c48 | |||
| 88b0d92300 | |||
| e570e82f90 | |||
| 85174c1fab | |||
| 7a895c76a6 | |||
| dbf01e4fc5 | |||
| 227392d7f6 | |||
| f8f2fe4673 | |||
| 4d16fa4184 | |||
| 5e69d6e5c5 | |||
|
|
a0de9cbb61 | ||
|
|
192a7a5d2f | ||
| dfd596cd56 | |||
| b92b1e73ce | |||
| 8ca151574a | |||
|
|
9f4447fa79 | ||
| 197c84f29c | |||
|
|
29d274e05b | ||
|
|
e558fcabc0 | ||
| 1ee13ab73d | |||
| 4414480d6e | |||
| 2ca121cbac | |||
| 25a5db7337 | |||
|
|
134ee3f59d | ||
|
|
914b1ec5ed | ||
|
|
86eaca953e | ||
|
|
a22479dffd | ||
|
|
bb455b2c45 | ||
|
|
9b64746c2e | ||
|
|
34080a0ee0 | ||
|
|
ff0f8d6fad | ||
|
|
988a05e94d | ||
|
|
5b9be577e7 | ||
|
|
615f708600 | ||
|
|
ba71eee97f | ||
|
|
06e0b8efe1 | ||
|
|
7a490e4599 | ||
|
|
8b6af5fa21 | ||
| 55486de524 | |||
|
|
836af4e1ae | ||
|
|
9352d06e73 |
@ -1,16 +0,0 @@
|
||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# For the full list of supported browsers by the Angular framework, please see:
|
||||
# https://angular.io/guide/browser-support
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 Safari major versions
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
||||
58
.gitignore
vendored
58
.gitignore
vendored
@ -1,42 +1,42 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
angular/dist
|
||||
angular/tmp
|
||||
angular/out-tsc
|
||||
angular/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
angular/node_modules
|
||||
angular/npm-debug.log
|
||||
angular/yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
angular/.idea/
|
||||
angular/.project
|
||||
angular/.classpath
|
||||
angular/.c9/
|
||||
angular/*.launch
|
||||
angular/.settings/
|
||||
angular/*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
angular/.vscode/*
|
||||
angular/!.vscode/settings.json
|
||||
angular/!.vscode/tasks.json
|
||||
angular/!.vscode/launch.json
|
||||
angular/!.vscode/extensions.json
|
||||
angular/.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
angular/.angular/cache
|
||||
angular/.sass-cache/
|
||||
angular/connect.lock
|
||||
angular/coverage
|
||||
angular/libpeerconnection.log
|
||||
angular/testem.log
|
||||
angular/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
angular/.DS_Store
|
||||
angular/Thumbs.db
|
||||
|
||||
4
.vscode/extensions.json
vendored
4
.vscode/extensions.json
vendored
@ -1,4 +0,0 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||
"recommendations": ["angular.ng-template"]
|
||||
}
|
||||
20
.vscode/launch.json
vendored
20
.vscode/launch.json
vendored
@ -1,20 +0,0 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ng serve",
|
||||
"type": "pwa-chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: start",
|
||||
"url": "http://localhost:4200/"
|
||||
},
|
||||
{
|
||||
"name": "ng test",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: test",
|
||||
"url": "http://localhost:9876/debug.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
42
.vscode/tasks.json
vendored
42
.vscode/tasks.json
vendored
@ -1,42 +0,0 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
30
Jenkinsfile
vendored
Normal file
30
Jenkinsfile
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
env.HL_BUILD_MODE = "jenkins"
|
||||
|
||||
node('Palladium'){
|
||||
|
||||
stage('get new version to repo') {
|
||||
checkout scm
|
||||
if (lastCommitIsBumpCommit()) {
|
||||
currentBuild.result = 'ABORTED'
|
||||
error('Последний коммит - результат сборки jenkins')
|
||||
}
|
||||
|
||||
//sh "git submodule update --init --recursive"
|
||||
//sh "git submodule update --remote --merge"
|
||||
}
|
||||
stage("build and publish"){
|
||||
dir('angular'){
|
||||
sh label: '', script: 'npm i'
|
||||
sh label: '', script: 'npm run build'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean lastCommitIsBumpCommit() {
|
||||
lastCommit = sh([script: 'git log -1', returnStdout: true])
|
||||
if (lastCommit.contains("Author: jenkins")) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
122
angular.json
122
angular.json
@ -1,122 +0,0 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"fashion-logica": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "/var/www/lk/fashion-logica",
|
||||
"baseHref": "/fashion-logica/",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets",
|
||||
"src/manifest.webmanifest",
|
||||
"src/firebase-messaging-sw.js"
|
||||
],
|
||||
"styles": [
|
||||
"node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css",
|
||||
"node_modules/primeicons/primeicons.css",
|
||||
"node_modules/primeng/resources/primeng.min.css",
|
||||
"node_modules/ngx-sharebuttons/themes/modern.scss",
|
||||
"node_modules/ngx-sharebuttons/themes/material.scss",
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [],
|
||||
"serviceWorker": true,
|
||||
"ngswConfigPath": "ngsw-config.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "4mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"optimization": false,
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true,
|
||||
"namedChunks": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "fashion-logica:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "fashion-logica:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "fashion-logica:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/firebase-messaging-sw.js",
|
||||
"src/assets",
|
||||
"src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"analytics": false
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
# FashionLogica
|
||||
# CoffeeLikeMore
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.0.6.
|
||||
|
||||
131
angular/angular.json
Normal file
131
angular/angular.json
Normal file
@ -0,0 +1,131 @@
|
||||
{
|
||||
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"coffee-like-more": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
},
|
||||
"i18n": {
|
||||
"sourceLocale": "ru-RU"
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "/var/www/html/lk.crm4retail.ru/more",
|
||||
"baseHref": "/",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets",
|
||||
"src/manifest.webmanifest",
|
||||
"src/firebase-messaging-sw.js",
|
||||
"src/sw-master.js",
|
||||
"src/sw-custom.js"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
"node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css",
|
||||
"node_modules/primeicons/primeicons.css",
|
||||
"node_modules/primeng/resources/primeng.min.css",
|
||||
"node_modules/ngx-sharebuttons/themes/modern.scss",
|
||||
"node_modules/ngx-sharebuttons/themes/material.scss",
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [],
|
||||
"serviceWorker": true,
|
||||
"ngswConfigPath": "ngsw-config.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "4mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"optimization": false,
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true,
|
||||
"namedChunks": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "coffee-like-more:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "coffee-like-more:build:development",
|
||||
"proxyConfig": "proxy.confi.json"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "coffee-like-more:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/firebase-messaging-sw.js",
|
||||
"src/assets",
|
||||
"src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/purple-green.css",
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"analytics": false
|
||||
}
|
||||
}
|
||||
11836
package-lock.json → angular/package-lock.json
generated
11836
package-lock.json → angular/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
63
angular/package.json
Normal file
63
angular/package.json
Normal file
@ -0,0 +1,63 @@
|
||||
{
|
||||
"name": "coffee-like-more",
|
||||
"version": "0.0.2",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve --host 192.168.0.14",
|
||||
"build": "ng build --aot --configuration production --output-hashing none",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^15.2.9",
|
||||
"@angular/cdk": "^15.2.9",
|
||||
"@angular/common": "^15.2.9",
|
||||
"@angular/compiler": "^15.2.9",
|
||||
"@angular/core": "^15.2.9",
|
||||
"@angular/fire": "^7.5.0",
|
||||
"@angular/forms": "^15.2.9",
|
||||
"@angular/material": "^15.2.9",
|
||||
"@angular/material-moment-adapter": "^15.2.9",
|
||||
"@angular/platform-browser": "^15.2.9",
|
||||
"@angular/platform-browser-dynamic": "^15.2.9",
|
||||
"@angular/router": "^15.2.9",
|
||||
"@angular/service-worker": "^15.2.9",
|
||||
"@fortawesome/angular-fontawesome": "^0.12.1",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.2.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.2.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.0",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/web": "^0.0.99",
|
||||
"angular-moment-timezone": "^1.7.1",
|
||||
"angular2-text-mask": "^9.0.0",
|
||||
"barcode-2-svg": "^0.3.3",
|
||||
"firebase": "^9.9.3",
|
||||
"google-libphonenumber": "^3.2.30",
|
||||
"jsbarcode": "^3.11.5",
|
||||
"libphonenumber-js": "^1.10.28",
|
||||
"ng-qrcode": "^8.0.1",
|
||||
"ngx-mat-intl-tel-input": "^5.0.0",
|
||||
"ngx-sharebuttons": "^11.0.0",
|
||||
"primeicons": "^5.0.0",
|
||||
"primeng": "^15.4.1",
|
||||
"rxjs": "~7.5.0",
|
||||
"tslib": "^2.3.0",
|
||||
"uuid": "^8.3.2",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^15.2.8",
|
||||
"@angular/cli": "~15.2.8",
|
||||
"@angular/compiler-cli": "^15.2.9",
|
||||
"@types/google-libphonenumber": "^7.4.23",
|
||||
"@types/jasmine": "~4.0.0",
|
||||
"jasmine-core": "~4.1.0",
|
||||
"karma": "~6.3.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.0.0",
|
||||
"karma-jasmine-html-reporter": "~1.7.0",
|
||||
"typescript": "4.8"
|
||||
}
|
||||
}
|
||||
38
angular/proxy.confi.json
Normal file
38
angular/proxy.confi.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"/api": {
|
||||
"target": "https://apple-push-notifications.it-retail.tech/apns/api",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/api": ""
|
||||
},
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/icard-proxy": {
|
||||
"target": "https://sakura.lk.crm4retail.ru/api/icard-proxy",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/icard-proxy": ""
|
||||
},
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/static": {
|
||||
"target": "https://sakura.lk.crm4retail.ru/static",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/static": ""
|
||||
},
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/it-retail": {
|
||||
"target": "https://sakura.lk.crm4retail.ru/it-retail",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/it-retail": ""
|
||||
},
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
}
|
||||
}
|
||||
20
angular/src/app/app-routing.module.ts
Normal file
20
angular/src/app/app-routing.module.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { MainComponent } from './pages/main/main.component';
|
||||
import { AuthGuard } from './guards/auth-guard.guard';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: () => null as any,
|
||||
// component: GuestCardComponent,
|
||||
// canActivate: [AuthGuard]
|
||||
},
|
||||
// { path: '**', component: NotFoundComponent }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
2
angular/src/app/app.component.html
Normal file
2
angular/src/app/app.component.html
Normal file
@ -0,0 +1,2 @@
|
||||
<router-outlet></router-outlet>
|
||||
<p-toast position="top-center"></p-toast>
|
||||
@ -20,16 +20,16 @@ describe('AppComponent', () => {
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'fashion-logica'`, () => {
|
||||
it(`should have as title 'coffee-like-more'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title).toEqual('fashion-logica');
|
||||
expect(app.title).toEqual('coffee-like-more');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('.content span')?.textContent).toContain('fashion-logica app is running!');
|
||||
expect(compiled.querySelector('.content span')?.textContent).toContain('coffee-like-more app is running!');
|
||||
});
|
||||
});
|
||||
64
angular/src/app/app.component.ts
Normal file
64
angular/src/app/app.component.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { RouteConfigLoadStart, Router } from '@angular/router';
|
||||
import { AuthService } from './services/auth.service';
|
||||
import { CookiesService } from './services/cookies.service';
|
||||
import { JsonrpcService, RpcService } from './services/jsonrpc.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
title = 'Coffee Like More';
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private cookiesService: CookiesService,
|
||||
private jsonRpcService: JsonrpcService,
|
||||
private authService: AuthService,
|
||||
) {
|
||||
this.router.events.subscribe((x) => {
|
||||
if (x instanceof RouteConfigLoadStart && x.route.path === '') {
|
||||
x.route.loadChildren = () => {
|
||||
switch (this.cookiesService.getItem('presentation-option')) {
|
||||
case 'first':
|
||||
return import(
|
||||
'./presentation-options/first-option/first-option.module'
|
||||
).then((mod) => mod.FirstOptionModule);
|
||||
|
||||
default:
|
||||
return import(
|
||||
'./presentation-options/default-option/default-option.module'
|
||||
).then((mod) => mod.DefaultOptionModule);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getAdditionalInfo() {
|
||||
return this.jsonRpcService.rpc(
|
||||
{
|
||||
method: 'getAdditionalInfo',
|
||||
params: [],
|
||||
},
|
||||
RpcService.authService,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.authService.authorized) {
|
||||
this.authService.getUserInfo();
|
||||
}
|
||||
this.getAdditionalInfo().subscribe({
|
||||
next: (value) => {
|
||||
this.cookiesService.setCookie('presentation-option', value?.data?.interface_id || 'default')
|
||||
},
|
||||
error: (err) => {
|
||||
console.error(err);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
93
angular/src/app/app.constants.ts
Normal file
93
angular/src/app/app.constants.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import { OrderStatus, Page, PageCode, lvlPeriod } from "./interface/data";
|
||||
|
||||
export const PageList: Page[] = [
|
||||
{
|
||||
code: PageCode.Auth,
|
||||
name: 'Вход',
|
||||
resName: 'auth',
|
||||
onSideBar: false,
|
||||
},
|
||||
{
|
||||
code: PageCode.Orders,
|
||||
name: 'Заказы',
|
||||
resName: 'orders',
|
||||
onSideBar: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const PageListWithBonus: Page[] = [
|
||||
{
|
||||
code: PageCode.Auth,
|
||||
name: 'Вход',
|
||||
resName: 'auth',
|
||||
onSideBar: false,
|
||||
},
|
||||
{
|
||||
code: PageCode.BonusProgram,
|
||||
name: 'Ваша карта лояльности',
|
||||
description: '',
|
||||
resName: 'bonus-program',
|
||||
onSideBar: true,
|
||||
},
|
||||
{
|
||||
code: PageCode.Orders,
|
||||
name: 'Ваши чеки',
|
||||
description: '',
|
||||
resName: 'orders',
|
||||
onSideBar: true,
|
||||
},
|
||||
{
|
||||
code: PageCode.UserData,
|
||||
name: 'Заполнить анкету',
|
||||
description: '',
|
||||
resName: 'user-data',
|
||||
onSideBar: true
|
||||
},
|
||||
{
|
||||
code: PageCode.RefSystem,
|
||||
name: 'Пригласить друга',
|
||||
description: '',
|
||||
resName: 'ref-system',
|
||||
onSideBar: true,
|
||||
}
|
||||
];
|
||||
|
||||
export const orderStatuses: OrderStatus = {
|
||||
'Cancelled': 'Отменен',
|
||||
'InProcessing': 'В обработке',
|
||||
'Unconfirmed': 'Принят',
|
||||
'WaitCooking': 'Принят',
|
||||
'ReadyForCooking': 'Принят',
|
||||
'CookingStarted': 'Готовится',
|
||||
'CookingCompleted': 'Приготовлен',
|
||||
'Waiting': 'В пути',
|
||||
'OnWay': 'В пути',
|
||||
'Delivered': 'Выполнен',
|
||||
'Closed': 'Выполнен',
|
||||
};
|
||||
|
||||
export const lvlPeriods: lvlPeriod[] = [
|
||||
{
|
||||
percent: 3,
|
||||
start: 0,
|
||||
end: 1600,
|
||||
color: '#f2c94c'
|
||||
},
|
||||
{
|
||||
percent: 6,
|
||||
start: 1601,
|
||||
end: 3600,
|
||||
color: '#f2994a'
|
||||
},
|
||||
{
|
||||
percent: 10,
|
||||
start: 3601,
|
||||
end: 8600,
|
||||
color: '#6fcf97'
|
||||
},
|
||||
{
|
||||
percent: 15,
|
||||
start: 8601,
|
||||
color: '#6fcf97'
|
||||
},
|
||||
]
|
||||
@ -1,15 +1,14 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { LoyalityProgramComponent } from './pages/loyality-program/loyality-program.component';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { MainComponent } from './pages/main/main.component';
|
||||
import { NavbarComponent } from './components/navbar/navbar.component';
|
||||
import { CardComponent } from './components/card/card.component';
|
||||
import {InputMaskModule} from "primeng/inputmask";
|
||||
import { InputMaskModule } from 'primeng/inputmask';
|
||||
import { AuthComponent } from './pages/account/auth/auth.component';
|
||||
import {ProgressSpinnerModule} from "primeng/progressspinner";
|
||||
import { ProgressSpinnerModule } from 'primeng/progressspinner';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { AccountComponent } from './pages/account/account.component';
|
||||
import { ExitComponent } from './components/exit/exit.component';
|
||||
@ -17,21 +16,40 @@ import { HttpClientModule } from '@angular/common/http';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { DialogService } from 'primeng/dynamicdialog';
|
||||
import { BonusProgramComponent } from './pages/account/bonus-program/bonus-program.component';
|
||||
import { OrdersComponent } from './pages/account/orders/orders.component';
|
||||
import { OrderInfoComponent } from './components/order-info/order-info.component';
|
||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||
import { environment } from '../environments/environment';
|
||||
import { AngularFireModule } from '@angular/fire/compat';
|
||||
import { AngularFireMessagingModule } from '@angular/fire/compat/messaging';
|
||||
import {ToastModule} from 'primeng/toast';
|
||||
import { ToastModule } from 'primeng/toast';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { FooterButtonsComponent } from './components/footer-buttons/footer-buttons.component';
|
||||
import { UserDataComponent } from './pages/account/user-data/user-data.component';
|
||||
import { RefSystemComponent } from './pages/account/ref-system/ref-system.component';
|
||||
import { QRCodeModule } from 'angularx-qrcode';
|
||||
import { ShareButtonsModule } from 'ngx-sharebuttons/buttons';
|
||||
import { ShareButtonsModule } from 'ngx-sharebuttons/buttons';
|
||||
import { ShareIconsModule } from 'ngx-sharebuttons/icons';
|
||||
import { MessagingService } from './services/messaging.service';
|
||||
import { NotFoundComponent } from './pages/not-found/not-found.component';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { GuestCardComponent } from './pages/guest-card/guest-card.component';
|
||||
import { QrCodeModule } from 'ng-qrcode';
|
||||
import { AccordionComponent } from './components/accordion/accordion.component';
|
||||
import { LastOrderComponent } from './components/last-order/last-order.component';
|
||||
import { InviteFriendsComponent } from './components/invite-friends/invite-friends.component';
|
||||
import { FooterComponent } from './components/footer/footer.component';
|
||||
import { SocialMediaButtonsComponent } from './components/social-media-buttons/social-media-buttons.component';
|
||||
import { LoginComponent } from './pages/login/login.component';
|
||||
import { NgxMatIntlTelInputComponent } from 'ngx-mat-intl-tel-input';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import {
|
||||
MAT_BOTTOM_SHEET_DATA,
|
||||
MatBottomSheetModule,
|
||||
MatBottomSheetRef,
|
||||
} from '@angular/material/bottom-sheet';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { DirectivesModule } from './directives/directives.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -43,44 +61,61 @@ import { MessagingService } from './services/messaging.service';
|
||||
AccountComponent,
|
||||
ExitComponent,
|
||||
BonusProgramComponent,
|
||||
OrdersComponent,
|
||||
OrderInfoComponent,
|
||||
FooterButtonsComponent,
|
||||
UserDataComponent,
|
||||
RefSystemComponent
|
||||
RefSystemComponent,
|
||||
NotFoundComponent,
|
||||
GuestCardComponent,
|
||||
AccordionComponent,
|
||||
LastOrderComponent,
|
||||
InviteFriendsComponent,
|
||||
FooterComponent,
|
||||
SocialMediaButtonsComponent,
|
||||
LoginComponent,
|
||||
// FocusNextInputDirective,
|
||||
LoyalityProgramComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
RouterModule.forRoot([
|
||||
{
|
||||
path: '**',
|
||||
component: MainComponent
|
||||
}
|
||||
]),
|
||||
InputMaskModule,
|
||||
ProgressSpinnerModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
BrowserAnimationsModule,
|
||||
BrowserModule,
|
||||
ServiceWorkerModule.register('ngsw-worker.js', {
|
||||
ServiceWorkerModule.register('/sw-master.js', {
|
||||
enabled: environment.production,
|
||||
// Register the ServiceWorker as soon as the application is stable
|
||||
// or after 30 seconds (whichever comes first).
|
||||
registrationStrategy: 'registerWhenStable:30000'
|
||||
registrationStrategy: 'registerWhenStable:30000',
|
||||
}),
|
||||
AngularFireModule.initializeApp(environment.firebase),
|
||||
AngularFireMessagingModule,
|
||||
ToastModule,
|
||||
ReactiveFormsModule,
|
||||
QRCodeModule,
|
||||
ShareButtonsModule.withConfig({
|
||||
debug: true
|
||||
debug: true,
|
||||
}),
|
||||
ShareIconsModule
|
||||
ShareIconsModule,
|
||||
MatIconModule,
|
||||
QrCodeModule,
|
||||
NgxMatIntlTelInputComponent,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatSnackBarModule,
|
||||
MatBottomSheetModule,
|
||||
MatProgressSpinnerModule,
|
||||
DirectivesModule,
|
||||
],
|
||||
providers: [DialogService, MessageService, MessagingService ],
|
||||
bootstrap: [AppComponent]
|
||||
providers: [
|
||||
DialogService,
|
||||
MessageService,
|
||||
MessagingService,
|
||||
{ provide: MatBottomSheetRef, useValue: {} },
|
||||
{ provide: MAT_BOTTOM_SHEET_DATA, useValue: {} },
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule { }
|
||||
@ -0,0 +1,7 @@
|
||||
<div class="tab">
|
||||
<input [id]="'tab-' + guid" type="checkbox" name="tabs" />
|
||||
<label [for]="'tab-' + guid">{{ header }}</label>
|
||||
<div class="tab-content">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,85 @@
|
||||
:host {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tab {
|
||||
position: relative;
|
||||
margin-bottom: 1px;
|
||||
width: 100%;
|
||||
color: var(--text-color);
|
||||
overflow: hidden;
|
||||
border-bottom: solid 1px #bdbdbd;
|
||||
}
|
||||
.tab input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
.tab label {
|
||||
display: block;
|
||||
padding: 20px 26px;
|
||||
position: relative;
|
||||
background: #ffffff00;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
letter-spacing: -0.5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
// .tab label:before {
|
||||
// display: block;
|
||||
// content: " ";
|
||||
// padding-top: 5px;
|
||||
// }
|
||||
.tab-content {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
background: #292929;
|
||||
-webkit-transition: all 0.35s;
|
||||
-o-transition: all 0.35s;
|
||||
transition: all 0.35s;
|
||||
border-top: solid 1px #bdbdbd;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.tab-content p {
|
||||
margin: 1em;
|
||||
}
|
||||
/* :checked */
|
||||
.tab input:checked ~ .tab-content {
|
||||
padding: 16px 26px;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
/* Icon */
|
||||
.tab label::after {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
line-height: 3;
|
||||
text-align: center;
|
||||
-webkit-transition: all 0.35s;
|
||||
-o-transition: all 0.35s;
|
||||
transition: all 0.35s;
|
||||
height: 100%;
|
||||
margin-right: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.tab input[type="checkbox"] + label::after {
|
||||
content: "+";
|
||||
}
|
||||
.tab input[type="radio"] + label::after {
|
||||
content: "\25BC";
|
||||
}
|
||||
.tab input[type="checkbox"]:checked + label::after {
|
||||
transform: rotate(315deg);
|
||||
}
|
||||
.tab input[type="radio"]:checked + label::after {
|
||||
transform: rotateX(180deg);
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AccordionComponent } from './accordion.component';
|
||||
|
||||
describe('AccordionComponent', () => {
|
||||
let component: AccordionComponent;
|
||||
let fixture: ComponentFixture<AccordionComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ AccordionComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AccordionComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
23
angular/src/app/components/accordion/accordion.component.ts
Normal file
23
angular/src/app/components/accordion/accordion.component.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export interface IAccordionData {
|
||||
header: string;
|
||||
body: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-accordion[header]',
|
||||
templateUrl: './accordion.component.html',
|
||||
styleUrls: ['./accordion.component.scss']
|
||||
})
|
||||
export class AccordionComponent implements OnInit {
|
||||
@Input() header!: string
|
||||
public guid: string = uuidv4()
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
7
angular/src/app/components/exit/exit.component.html
Normal file
7
angular/src/app/components/exit/exit.component.html
Normal file
@ -0,0 +1,7 @@
|
||||
<span class="example-pizza-party" matSnackBarLabel>
|
||||
Вы действительно хотите выйти?
|
||||
</span>
|
||||
<div class="buttons-container">
|
||||
<button mat-button (click)="rejection()" style="background: red;">Нет</button>
|
||||
<button mat-button (click)="logout()">Да</button>
|
||||
</div>
|
||||
22
angular/src/app/components/exit/exit.component.scss
Normal file
22
angular/src/app/components/exit/exit.component.scss
Normal file
@ -0,0 +1,22 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.buttons-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 62px;
|
||||
margin-top: 8px;
|
||||
background-color: var(--button-color);
|
||||
color: var(--button-text-color);
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
width: calc(50% - 12px);
|
||||
}
|
||||
21
angular/src/app/components/exit/exit.component.ts
Normal file
21
angular/src/app/components/exit/exit.component.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { MatBottomSheetRef } from '@angular/material/bottom-sheet';
|
||||
|
||||
@Component({
|
||||
selector: 'app-exit',
|
||||
templateUrl: './exit.component.html',
|
||||
styleUrls: ['./exit.component.scss'],
|
||||
})
|
||||
export class ExitComponent {
|
||||
constructor(
|
||||
private _bottomSheetRef: MatBottomSheetRef<ExitComponent>
|
||||
) {}
|
||||
|
||||
rejection() {
|
||||
this._bottomSheetRef.dismiss(false)
|
||||
}
|
||||
|
||||
logout() {
|
||||
this._bottomSheetRef.dismiss(true)
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,11 @@
|
||||
<div class="footer-buttons-container">
|
||||
<!-- *ngIf="deferredPrompt && token?.length" -->
|
||||
<button
|
||||
<!-- <button
|
||||
*ngIf="((deviceType == 'android' && deferredPrompt) || deviceType == 'ios') && token?.length"
|
||||
class="footer-buttons-container__button download"
|
||||
(click)="downloadPWA()"
|
||||
>
|
||||
<img src="./assets/download.svg" alt="download" />
|
||||
</button>
|
||||
</button> -->
|
||||
<button
|
||||
*ngIf="!isPermissionNotifications && token?.length"
|
||||
class="footer-buttons-container__button"
|
||||
@ -15,9 +15,9 @@
|
||||
align-items: center;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: #09467f;
|
||||
border: solid #fff 1px !important;
|
||||
color: #fff;
|
||||
background: var(--text-color);
|
||||
border: solid var(--text-color) 1px !important;
|
||||
color: var(--text-color);
|
||||
border-radius: 100%;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2px;
|
||||
@ -0,0 +1,24 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { MessageService } from 'primeng/api';
|
||||
|
||||
@Component({
|
||||
selector: 'app-footer-buttons',
|
||||
templateUrl: './footer-buttons.component.html',
|
||||
styleUrls: ['./footer-buttons.component.scss']
|
||||
})
|
||||
export class FooterButtonsComponent implements OnInit {
|
||||
@Input() token!: string;
|
||||
@Input() isPermissionNotifications!: boolean;
|
||||
@Output() requestingPermission = new EventEmitter<null>();
|
||||
public deviceType: 'ios' | 'android' | null = null;
|
||||
|
||||
constructor(
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
requestPermission() {
|
||||
this.requestingPermission.emit(null)
|
||||
}
|
||||
}
|
||||
4
angular/src/app/components/footer/footer.component.html
Normal file
4
angular/src/app/components/footer/footer.component.html
Normal file
@ -0,0 +1,4 @@
|
||||
<img src="/assets/logo.svg" alt="Логотип">
|
||||
<h3>Горячая линия</h3>
|
||||
<a class="phone-number" href="tel:88003334130">8 (800) 333-41-30</a>
|
||||
<app-social-media-buttons></app-social-media-buttons>
|
||||
28
angular/src/app/components/footer/footer.component.scss
Normal file
28
angular/src/app/components/footer/footer.component.scss
Normal file
@ -0,0 +1,28 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
padding-bottom: 100px;
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-family: Montserrat;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
letter-spacing: -0.24px;
|
||||
color: #6a737c;
|
||||
}
|
||||
.phone-number {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
letter-spacing: -0.408px;
|
||||
color: #d9d9d9;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
23
angular/src/app/components/footer/footer.component.spec.ts
Normal file
23
angular/src/app/components/footer/footer.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { FooterComponent } from './footer.component';
|
||||
|
||||
describe('FooterComponent', () => {
|
||||
let component: FooterComponent;
|
||||
let fixture: ComponentFixture<FooterComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ FooterComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(FooterComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
15
angular/src/app/components/footer/footer.component.ts
Normal file
15
angular/src/app/components/footer/footer.component.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-footer',
|
||||
templateUrl: './footer.component.html',
|
||||
styleUrls: ['./footer.component.scss']
|
||||
})
|
||||
export class FooterComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<h2>Пригласи друзей!</h2>
|
||||
<div class="container">
|
||||
<p>
|
||||
Пригласи друзей зарегистрироваться в приложении по твоему уникальному коду и
|
||||
получи бонусы, когда они совершат первую покупку.
|
||||
</p>
|
||||
<button class="share" (click)="share()" [disabled]="loading">
|
||||
<ng-container *ngIf="loading">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="spinner; context: { $implicit: 24 }"
|
||||
></ng-container>
|
||||
</ng-container>
|
||||
<mat-icon
|
||||
*ngIf="!loading"
|
||||
aria-hidden="false"
|
||||
aria-label="Share"
|
||||
fontIcon="share"
|
||||
style="color: #fff"
|
||||
></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ng-template #spinner let-diameter>
|
||||
<mat-spinner [strokeWidth]="3" [diameter]="diameter"></mat-spinner>
|
||||
</ng-template>
|
||||
@ -0,0 +1,34 @@
|
||||
:host {
|
||||
padding: 16px;
|
||||
h2 {
|
||||
font-family: Montserrat;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
letter-spacing: -0.408px;
|
||||
}
|
||||
|
||||
& .container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
|
||||
p {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.share {
|
||||
width: 72px;
|
||||
height: 48px;
|
||||
background: var(--button-color);
|
||||
padding: 8px 26px;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { InviteFriendsComponent } from './invite-friends.component';
|
||||
|
||||
describe('InviteFriendsComponent', () => {
|
||||
let component: InviteFriendsComponent;
|
||||
let fixture: ComponentFixture<InviteFriendsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ InviteFriendsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(InviteFriendsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,54 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
@Component({
|
||||
selector: 'app-invite-friends',
|
||||
templateUrl: './invite-friends.component.html',
|
||||
styleUrls: ['./invite-friends.component.scss']
|
||||
})
|
||||
export class InviteFriendsComponent implements OnInit {
|
||||
public refUrl: string = `${environment.defaultUrl}/?refUserId=`
|
||||
public loading: boolean = true;
|
||||
private shareData: ShareData = {
|
||||
title: ''
|
||||
}
|
||||
|
||||
constructor(
|
||||
private jsonrpc: JsonrpcService,
|
||||
private messageService: MessageService
|
||||
) { }
|
||||
|
||||
async ngOnInit() {
|
||||
const accountData = (await lastValueFrom(
|
||||
this.jsonrpc
|
||||
.rpc(
|
||||
{
|
||||
method: 'getTokenData',
|
||||
params: [],
|
||||
},
|
||||
RpcService.authService,
|
||||
true
|
||||
)
|
||||
)).data
|
||||
|
||||
this.refUrl += accountData.user_id
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
share() {
|
||||
if (navigator.share) {
|
||||
navigator.share({
|
||||
title: document.title,
|
||||
text: "Coffee Like More",
|
||||
url: this.refUrl
|
||||
})
|
||||
.then(() => console.log('Successful share'))
|
||||
.catch((error) => {
|
||||
console.log('Error sharing:', error)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
<h2>Ваш предыдущий заказ</h2>
|
||||
<div class="info-order">
|
||||
<p class="flex"><span>Дата: </span>
|
||||
<span *ngIf="!loading">{{(lastOrder?.transactionCreateDate | date:'dd.MM.yyyyг.') || 'Данные не найдены'}}</span>
|
||||
<ng-container *ngIf="loading">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="spinner; context: { $implicit: 24 }"
|
||||
></ng-container>
|
||||
</ng-container>
|
||||
</p>
|
||||
<p class="flex"><span>На сумму: </span>
|
||||
<span *ngIf="!loading">{{lastOrder?.orderSum ? lastOrder?.orderSum + ' ₽' : 'Данные не найдены'}}</span>
|
||||
<ng-container *ngIf="loading">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="spinner; context: { $implicit: 24 }"
|
||||
></ng-container>
|
||||
</ng-container>
|
||||
</p>
|
||||
</div>
|
||||
<a href="https://yandex.ru/profile/151770398186" target="_blank">
|
||||
<button class="evaluate-order">Оценить заказ</button>
|
||||
</a>
|
||||
<p class="info">
|
||||
Списание бонусов возможно на любые категории. Бонусами можно оплатить 100%
|
||||
суммы покупки. Бонусы начисляются только на напитки с учётом добавок.
|
||||
Неиспользованные бонусы сгорают в течение 90 дней.
|
||||
</p>
|
||||
|
||||
<ng-template #spinner let-diameter>
|
||||
<mat-spinner [strokeWidth]="3" [diameter]="diameter"></mat-spinner>
|
||||
</ng-template>
|
||||
@ -0,0 +1,52 @@
|
||||
:host {
|
||||
padding: 24px 16px 0px;
|
||||
|
||||
& > h2 {
|
||||
font-family: Montserrat;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.24px;
|
||||
}
|
||||
|
||||
& > .info-order {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
span {
|
||||
color: #828282;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.evaluate-order {
|
||||
margin: 24px 0;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
border: 2px solid #28af49;
|
||||
border-radius: 6px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
letter-spacing: -0.408px;
|
||||
background-color: transparent;
|
||||
color: #28af49;
|
||||
}
|
||||
|
||||
.info {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LastOrderComponent } from './last-order.component';
|
||||
|
||||
describe('LastOrderComponent', () => {
|
||||
let component: LastOrderComponent;
|
||||
let fixture: ComponentFixture<LastOrderComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ LastOrderComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LastOrderComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,18 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Purchase } from 'src/app/interface/data';
|
||||
|
||||
@Component({
|
||||
selector: 'app-last-order[lastOrder]',
|
||||
templateUrl: './last-order.component.html',
|
||||
styleUrls: ['./last-order.component.scss']
|
||||
})
|
||||
export class LastOrderComponent implements OnInit {
|
||||
@Input() lastOrder!: Purchase;
|
||||
@Input() loading!: boolean;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
5
angular/src/app/components/navbar/navbar.component.html
Normal file
5
angular/src/app/components/navbar/navbar.component.html
Normal file
@ -0,0 +1,5 @@
|
||||
<div class="container">
|
||||
<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>
|
||||
<div class="plug"></div>
|
||||
</div>
|
||||
33
angular/src/app/components/navbar/navbar.component.scss
Normal file
33
angular/src/app/components/navbar/navbar.component.scss
Normal file
@ -0,0 +1,33 @@
|
||||
:host {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
box-sizing: border-box;
|
||||
padding: 12px 16px;
|
||||
width: 100%;
|
||||
background: #231f20;
|
||||
box-shadow: 0px 8px 16px rgba(17, 17, 17, 0.5);
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.back-arrow {
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.plug {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
visibility: hidden;
|
||||
}
|
||||
.title {
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
21
angular/src/app/components/navbar/navbar.component.ts
Normal file
21
angular/src/app/components/navbar/navbar.component.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-navbar[title]',
|
||||
templateUrl: './navbar.component.html',
|
||||
styleUrls: ['./navbar.component.scss']
|
||||
})
|
||||
export class NavbarComponent implements OnInit {
|
||||
@Input() title: string = 'Название не задано'
|
||||
@Output() backEvent = new EventEmitter<null>();
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
back() {
|
||||
this.backEvent.emit(null)
|
||||
}
|
||||
|
||||
}
|
||||
@ -15,8 +15,7 @@
|
||||
}
|
||||
.step {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: #fff;
|
||||
height: 20px;
|
||||
border: 2px solid #acaca6;
|
||||
border-radius: 50%;
|
||||
transition: background 1s;
|
||||
@ -0,0 +1,3 @@
|
||||
<a *ngFor="let link of links" [href]="link.url" target="_blank">
|
||||
<img [src]="link.imgUrl" [alt]="link.label" />
|
||||
</a>
|
||||
@ -0,0 +1,15 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
justify-content: space-between;
|
||||
a {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 100%;
|
||||
background: #333333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SocialMediaButtonsComponent } from './social-media-buttons.component';
|
||||
|
||||
describe('SocialMediaButtonsComponent', () => {
|
||||
let component: SocialMediaButtonsComponent;
|
||||
let fixture: ComponentFixture<SocialMediaButtonsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SocialMediaButtonsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SocialMediaButtonsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,38 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
export interface ISocialMediaLink {
|
||||
url: string;
|
||||
imgUrl: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-social-media-buttons',
|
||||
templateUrl: './social-media-buttons.component.html',
|
||||
styleUrls: ['./social-media-buttons.component.scss']
|
||||
})
|
||||
export class SocialMediaButtonsComponent implements OnInit {
|
||||
public links: ISocialMediaLink[] = [
|
||||
{
|
||||
label: 'Инстаграм',
|
||||
url: 'https://www.instagram.com/',
|
||||
imgUrl: '/assets/social-media-icons/instagram.svg'
|
||||
},
|
||||
{
|
||||
label: 'ВК',
|
||||
url: 'https://vk.com/coffeelike_com',
|
||||
imgUrl: '/assets/social-media-icons/vk.svg'
|
||||
},
|
||||
{
|
||||
label: 'Youtube',
|
||||
url: 'https://www.youtube.com/c/coffeelikeru',
|
||||
imgUrl: '/assets/social-media-icons/youtube.svg'
|
||||
}
|
||||
]
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
13
angular/src/app/directives/directives.module.ts
Normal file
13
angular/src/app/directives/directives.module.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FocusNextInputDirective } from './focus-next-input.directive';
|
||||
import { DownloadAppDirective } from './download-app.directive';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule
|
||||
],
|
||||
declarations: [FocusNextInputDirective, DownloadAppDirective],
|
||||
exports: [FocusNextInputDirective, DownloadAppDirective]
|
||||
})
|
||||
export class DirectivesModule { }
|
||||
81
angular/src/app/directives/download-app.directive.ts
Normal file
81
angular/src/app/directives/download-app.directive.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
OnInit,
|
||||
Renderer2,
|
||||
} from '@angular/core';
|
||||
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { getTypeDevice, pwaInstalled } from 'src/app/utils';
|
||||
|
||||
@Directive({
|
||||
selector: '[appDownloadApp]',
|
||||
})
|
||||
export class DownloadAppDirective implements OnInit {
|
||||
public deviceType: 'ios' | 'android' | null = null;
|
||||
public deferredPrompt: any;
|
||||
|
||||
constructor(
|
||||
private messageService: MessageService,
|
||||
public renderer: Renderer2,
|
||||
private el: ElementRef,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
getTypeDevice();
|
||||
if (this.deviceType === 'ios') {
|
||||
this.el.nativeElement.style.display = 'block';
|
||||
}
|
||||
this.checkInstalled();
|
||||
}
|
||||
|
||||
async checkInstalled(): Promise<void> {
|
||||
if (window.matchMedia('(display-mode: standalone)').matches
|
||||
|| await pwaInstalled()) {
|
||||
this.el.nativeElement.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('window:beforeinstallprompt', ['$event'])
|
||||
onBeforeInstallPrompt(e: any) {
|
||||
e.preventDefault();
|
||||
this.deferredPrompt = e;
|
||||
if (this.deferredPrompt) {
|
||||
this.el.nativeElement.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('window:appinstalled', ['$event'])
|
||||
onAppInstalled(e: any) {
|
||||
// Prevent Chrome 67 and earlier from automatically showing the prompt
|
||||
e.preventDefault();
|
||||
// Stash the event so it can be triggered later.
|
||||
this.deferredPrompt = e;
|
||||
this.el.nativeElement.style.display = 'none';
|
||||
}
|
||||
|
||||
@HostListener('click', ['$event'])
|
||||
async downloadApp(event: MouseEvent) {
|
||||
if (!this.deferredPrompt) {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: 'Не поддерживается в Вашем браузере!',
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.deferredPrompt.prompt();
|
||||
this.deferredPrompt.userChoice.then((res: any) => {
|
||||
if (res.outcome === 'accepted') {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'success',
|
||||
summary: 'Спасибо за установку!',
|
||||
});
|
||||
}
|
||||
this.deferredPrompt = null;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
29
angular/src/app/directives/focus-next-input.directive.ts
Normal file
29
angular/src/app/directives/focus-next-input.directive.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Directive, EventEmitter, Input, Renderer2 } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appFocusNextInput]'
|
||||
})
|
||||
export class FocusNextInputDirective {
|
||||
@Input('appFocusNextInput') eventEmitter!: EventEmitter<string>;
|
||||
|
||||
constructor(private renderer: Renderer2) { }
|
||||
|
||||
ngOnInit() {
|
||||
// TODO:
|
||||
// 1: don't need to listen all events
|
||||
// 2: not working in safari
|
||||
this.eventEmitter.subscribe(elementId => {
|
||||
try {
|
||||
const element = this.renderer.selectRootElement(elementId)
|
||||
setTimeout(() => {
|
||||
element.focus();
|
||||
element.click();
|
||||
}, 0);
|
||||
} catch (ex) {
|
||||
(document.activeElement as HTMLElement).blur();
|
||||
// If the element doesn't exist or if the element disappears when this called then no need to do anything
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
16
angular/src/app/guards/auth-guard.guard.spec.ts
Normal file
16
angular/src/app/guards/auth-guard.guard.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AuthGuard } from './auth-guard.guard';
|
||||
|
||||
describe('AuthGuardGuard', () => {
|
||||
let guard: AuthGuard;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
guard = TestBed.inject(AuthGuard);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(guard).toBeTruthy();
|
||||
});
|
||||
});
|
||||
47
angular/src/app/guards/auth-guard.guard.ts
Normal file
47
angular/src/app/guards/auth-guard.guard.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
ActivatedRouteSnapshot,
|
||||
CanActivate,
|
||||
CanActivateChild,
|
||||
Router,
|
||||
RouterStateSnapshot,
|
||||
UrlTree,
|
||||
} from '@angular/router';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { CookiesService } from '../services/cookies.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthGuard implements CanActivate, CanActivateChild {
|
||||
constructor(private cookiesService: CookiesService, private router: Router) {}
|
||||
|
||||
canActivate(
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot
|
||||
):
|
||||
| Observable<boolean | UrlTree>
|
||||
| Promise<boolean | UrlTree>
|
||||
| boolean
|
||||
| UrlTree
|
||||
{
|
||||
if (this.cookiesService.getItem('token')) {
|
||||
return of(true);
|
||||
} else {
|
||||
this.router.navigate(['/login']);
|
||||
return of(false);
|
||||
}
|
||||
}
|
||||
|
||||
canActivateChild(
|
||||
childRoute: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot
|
||||
):
|
||||
| boolean
|
||||
| UrlTree
|
||||
| Observable<boolean | UrlTree>
|
||||
| Promise<boolean | UrlTree>
|
||||
{
|
||||
return this.canActivate(childRoute, state);
|
||||
}
|
||||
}
|
||||
222
angular/src/app/interface/data.ts
Normal file
222
angular/src/app/interface/data.ts
Normal file
@ -0,0 +1,222 @@
|
||||
export enum PageCode {
|
||||
Auth,
|
||||
Orders,
|
||||
BonusProgram,
|
||||
RefSystem,
|
||||
UserData,
|
||||
}
|
||||
|
||||
export interface Moment extends moment.Moment { }
|
||||
|
||||
export interface Page {
|
||||
code: PageCode;
|
||||
component?: any;
|
||||
name: string;
|
||||
description?: string;
|
||||
getMethod?: string;
|
||||
resName?: string;
|
||||
onSideBar: boolean;
|
||||
}
|
||||
|
||||
export interface UserDataForm {
|
||||
first_name: string;
|
||||
birthdate: string;
|
||||
gender: string;
|
||||
}
|
||||
|
||||
export interface BonusProgramAccount {
|
||||
BonusProgramName: string;
|
||||
BonusProgramTypeID: string;
|
||||
CardNumber: number;
|
||||
Bonuses: number;
|
||||
HoldedBonuses: number;
|
||||
BonusProgramAccounts: BonusProgramAccount[];
|
||||
DateBonusBurn: string;
|
||||
_links: any[];
|
||||
_embedded: any;
|
||||
}
|
||||
|
||||
export interface Purchase {
|
||||
PurchaseId?: string;
|
||||
CustomerId?: string;
|
||||
PurchaseDate: string;
|
||||
PurchaseState?: number;
|
||||
CardNumber?: number;
|
||||
Address?: string;
|
||||
CheckSummary?: number;
|
||||
BonusSummary?: number;
|
||||
ID: string;
|
||||
Transactions: Transaction[];
|
||||
IsSingleTransaction?: boolean;
|
||||
transactionCreateDate?: string;
|
||||
transactionType?: 'CancelPayFromWallet' | 'PayFromWallet' | 'RefillWallet';
|
||||
transactionSum: number;
|
||||
orderSum?: number;
|
||||
}
|
||||
|
||||
export interface Transaction {
|
||||
User: string;
|
||||
Purchase: string;
|
||||
Date: string;
|
||||
Value: number;
|
||||
TransactionType: number;
|
||||
UserBonusesSnapshot: number;
|
||||
BonusPercent: number;
|
||||
DateActiveBonus: string;
|
||||
AccountBonus: string;
|
||||
Bonus: string;
|
||||
ID: string;
|
||||
HasPurchase?: boolean;
|
||||
}
|
||||
|
||||
export interface OrderStatus {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface lvlPeriod {
|
||||
percent: number;
|
||||
start: number;
|
||||
end?: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
export interface DeliveryType {
|
||||
cost: number;
|
||||
title: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface AcceptedOrder {
|
||||
id: number;
|
||||
status: string;
|
||||
currency_symbol: string;
|
||||
total: number;
|
||||
address: {
|
||||
city: string;
|
||||
street: string;
|
||||
house: number;
|
||||
flat: number;
|
||||
};
|
||||
payment_method: string;
|
||||
shipping: {
|
||||
name: string;
|
||||
total: number;
|
||||
};
|
||||
date_created: string;
|
||||
items: OrderProduct[];
|
||||
}
|
||||
|
||||
export interface Product {
|
||||
id: number;
|
||||
name: string;
|
||||
price: string;
|
||||
image_url: string;
|
||||
image_gallery: string[];
|
||||
category_id: number;
|
||||
description?: string;
|
||||
stock_status: string;
|
||||
currency_symbol: string;
|
||||
modifier_data: Modifier[];
|
||||
short_description?: string;
|
||||
guid?: string;
|
||||
}
|
||||
|
||||
export interface Modifier {
|
||||
id: number;
|
||||
name: string;
|
||||
category_type: string;
|
||||
minimum_options: number;
|
||||
maximum_options: number;
|
||||
global_categories: string;
|
||||
required: number;
|
||||
options: Option[];
|
||||
allOptions?: Option[];
|
||||
}
|
||||
|
||||
export interface Option {
|
||||
id: number;
|
||||
name: string;
|
||||
price: string;
|
||||
prechecked: string;
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
export interface OrderProduct {
|
||||
id: number;
|
||||
amount: number;
|
||||
name: string;
|
||||
price: number;
|
||||
modifiers: {
|
||||
[name: string]: OrderModifier[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface OrderModifier {
|
||||
name: string;
|
||||
id: number;
|
||||
price: number;
|
||||
}
|
||||
|
||||
export interface DeliveryData {
|
||||
paymentMethod: PaymentMethod;
|
||||
deliveryDate: Date | null;
|
||||
deliveryType: DeliveryType | null;
|
||||
persons: number;
|
||||
comment: string;
|
||||
}
|
||||
|
||||
export interface PaymentMethod {
|
||||
type: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface UserData {
|
||||
first_name: string | null;
|
||||
last_name: string | null;
|
||||
street: string | null;
|
||||
house: string | null;
|
||||
flat: string | null;
|
||||
city: string;
|
||||
phone: string | null;
|
||||
}
|
||||
|
||||
export interface UserInfoCategory {
|
||||
id: string;
|
||||
isActive: boolean;
|
||||
isDefaultForNewGuests: boolean;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface UserInfoWallet {
|
||||
id: string;
|
||||
name: string;
|
||||
programType: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface UserInfoWalletBalance {
|
||||
balance: number;
|
||||
wallet: UserInfoWallet;
|
||||
}
|
||||
|
||||
export interface UserInfo {
|
||||
OrdersSum: number;
|
||||
categories: UserInfoCategory[];
|
||||
customer_level: number;
|
||||
birthday: string;
|
||||
id: string;
|
||||
name: string | null;
|
||||
phone: string;
|
||||
walletBalances: UserInfoWalletBalance[] | { error: ResponseError };
|
||||
}
|
||||
|
||||
export interface ResponseError {
|
||||
code: number;
|
||||
msg: string;
|
||||
}
|
||||
|
||||
export interface UpdateNewCustomerRequest {
|
||||
name: string;
|
||||
sex: string;
|
||||
birthday: string;
|
||||
}
|
||||
@ -1,5 +1,10 @@
|
||||
<div class="woocommerce">
|
||||
<div
|
||||
<div
|
||||
[ngClass]="{
|
||||
woocommerce: true,
|
||||
'auth-page': currentPage.code === PageCode.Auth
|
||||
}"
|
||||
>
|
||||
<!-- <div
|
||||
*ngIf="currentPage.code !== PageCode.Auth"
|
||||
class="top-left-attribute"
|
||||
></div>
|
||||
@ -40,6 +45,7 @@
|
||||
>
|
||||
<div class="container">
|
||||
<img
|
||||
style="width: 36px"
|
||||
src="{{ './assets/menu-icons/' + page.resName + '.png' }}"
|
||||
alt="Иконка меню"
|
||||
/>
|
||||
@ -50,6 +56,22 @@
|
||||
</div>
|
||||
</li>
|
||||
</ng-container>
|
||||
<li
|
||||
class="woocommerce-MyAccount-navigation-link download-app"
|
||||
appDownloadApp
|
||||
>
|
||||
<div class="container">
|
||||
<img
|
||||
style="width: 36px"
|
||||
src="./assets/menu-icons/download-app.png"
|
||||
alt="Иконка меню"
|
||||
/>
|
||||
<div class="menu-item-info">
|
||||
<a href="#">Установить приложение</a>
|
||||
<p>{{ page.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="woocommerce-MyAccount-navigation-link"
|
||||
(click)="logout($event)"
|
||||
@ -62,12 +84,11 @@
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</nav> -->
|
||||
<span
|
||||
class="version"
|
||||
[ngClass]="{
|
||||
version: true,
|
||||
bottom: currentPage.code === PageCode.Auth
|
||||
version: true
|
||||
}"
|
||||
>
|
||||
v{{ version }}
|
||||
@ -2,11 +2,19 @@
|
||||
.woocommerce {
|
||||
min-height: calc(100vh - 39px);
|
||||
padding: 20px 18px;
|
||||
|
||||
&.auth-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
nav {
|
||||
margin-bottom: 24px;
|
||||
margin-top: 26px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
ul {
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
@ -14,6 +22,7 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
|
||||
li {
|
||||
padding: 12px;
|
||||
width: 100%;
|
||||
@ -25,20 +34,29 @@
|
||||
box-shadow: 0px 0px 5px rgb(0 0 0 / 15%);
|
||||
color: #000;
|
||||
border-radius: 15px;
|
||||
|
||||
&.is-active {
|
||||
border: solid #09467f 1px;
|
||||
border: solid var(--main-color) 1px;
|
||||
// display: none;
|
||||
}
|
||||
|
||||
&.first {
|
||||
// border-radius: 7px 0 0 7px;
|
||||
}
|
||||
|
||||
&.download-app {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
|
||||
.menu-item-info {
|
||||
margin-left: 16px;
|
||||
& > a {
|
||||
|
||||
&>a {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
@ -48,7 +66,8 @@
|
||||
display: block;
|
||||
text-align-last: left;
|
||||
}
|
||||
& > p {
|
||||
|
||||
&>p {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
@ -58,25 +77,24 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li:nth-child(odd) {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.top-left-attribute {
|
||||
width: 10px;
|
||||
height: 5px;
|
||||
background: #09467f;
|
||||
background: var(--main-color);
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 69px;
|
||||
top: 103px;
|
||||
}
|
||||
|
||||
.version {
|
||||
opacity: 0.5;
|
||||
&.bottom {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@ import { ExitComponent } from '../../components/exit/exit.component';
|
||||
import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog';
|
||||
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-account',
|
||||
@ -70,10 +71,24 @@ export class AccountComponent implements OnInit {
|
||||
this.currentPage = this.pageList[1];
|
||||
}
|
||||
|
||||
refSystem() {
|
||||
async refSystem() {
|
||||
const additionalInfo = (await lastValueFrom(
|
||||
this.jsonRpcService.rpc({
|
||||
method: 'getAdditionalInfo',
|
||||
params: []
|
||||
}, RpcService.authService, true)
|
||||
)).data
|
||||
this.route.queryParams.subscribe((params) => {
|
||||
if (params['refCardNumber']) {
|
||||
this.refSystemId = params['refCardNumber'];
|
||||
if (params['refUserId']) {
|
||||
if (additionalInfo.refSystem?.code.length) {
|
||||
this.messageService.add({
|
||||
severity: 'custom',
|
||||
summary:
|
||||
'Вы уже зарегестрированы в реферальной программе!',
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.refSystemId = params['refUserId'];
|
||||
this.jsonRpcService
|
||||
.rpc(
|
||||
{
|
||||
@ -93,7 +108,7 @@ export class AccountComponent implements OnInit {
|
||||
next: () => {
|
||||
this.router.navigate([], {
|
||||
queryParams: {
|
||||
refCardNumber: null,
|
||||
refUserId: null,
|
||||
},
|
||||
queryParamsHandling: 'merge',
|
||||
});
|
||||
@ -117,6 +132,10 @@ export class AccountComponent implements OnInit {
|
||||
|
||||
changePage(event: MouseEvent, page: Page): void {
|
||||
event.preventDefault();
|
||||
if (page.resName === 'download-app') {
|
||||
|
||||
return
|
||||
}
|
||||
this.currentPage = page;
|
||||
// let params = new HttpParams();
|
||||
// params = params.append('activePage', this.currentPage.code);
|
||||
@ -147,7 +166,7 @@ export class AccountComponent implements OnInit {
|
||||
}
|
||||
|
||||
deleteToken(): void {
|
||||
this.cookiesService.deleteCookie('token');
|
||||
this.cookiesService.logout();
|
||||
// this.router.navigate(['.'], {
|
||||
// queryParams: {},
|
||||
// });
|
||||
@ -40,7 +40,7 @@
|
||||
></p-progressSpinner>
|
||||
</button>
|
||||
</p>
|
||||
<div class="decoration-pattern" style="background: url('./assets/card-decorative-pattern.png') no-repeat;"></div>
|
||||
<div class="decoration-pattern"></div>
|
||||
</form>
|
||||
</ng-container>
|
||||
<ng-template #confirmPhoneField>
|
||||
@ -54,8 +54,7 @@
|
||||
class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide"
|
||||
>
|
||||
<label for="code">
|
||||
Введите 4 последних цифры позвонившего вам номера телефона. На звонок
|
||||
отвечать не нужно.
|
||||
Введите 4 цифры из смс, которое пришло на Ваш номер телефона
|
||||
</label>
|
||||
<input
|
||||
pInputText
|
||||
@ -94,6 +93,6 @@
|
||||
<p style="color: red; margin: 0" *ngIf="errorConfirmCode">
|
||||
Пароль введен неверно
|
||||
</p>
|
||||
<div class="decoration-pattern" style="background: url('./assets/card-decorative-pattern.png') no-repeat;"></div>
|
||||
<div class="decoration-pattern"></div>
|
||||
</form>
|
||||
</ng-template>
|
||||
@ -102,5 +102,8 @@
|
||||
.decoration-pattern {
|
||||
width: calc(100% - 24px);
|
||||
height: 34px;
|
||||
background-image: url('../../../../assets/card-decorative-pattern.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
}
|
||||
@ -11,22 +11,19 @@
|
||||
<!-- <div class="imgs-row"></div>
|
||||
<div class="imgs-row" style="background-position-x: -22px;"></div>
|
||||
<div class="imgs-row"></div> -->
|
||||
<img src="./assets/card-decorative-pattern.svg" alt="" />
|
||||
<img src="./assets/card-decorative-pattern.png" alt="" />
|
||||
</div>
|
||||
<div class="card-container__content">
|
||||
<div class="info">
|
||||
<div *ngIf="accountData" class="row">
|
||||
<span class="key">Имя</span>
|
||||
<span class="value" *ngIf="userName.length">{{
|
||||
userName
|
||||
<span class="value">{{
|
||||
userName && userName.length ? userName : 'Не задано'
|
||||
}}</span>
|
||||
<span class="value" *ngIf="!userName.length"
|
||||
>Не задано</span
|
||||
>
|
||||
</div>
|
||||
<div *ngIf="accountData" class="row">
|
||||
<span class="key">Баланс карты</span>
|
||||
<span class="value">{{ accountData.Bonuses }}</span>
|
||||
<span class="value">{{ bonuses }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-container__barcode-container">
|
||||
@ -47,4 +44,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button *ngIf="deviceType == 'ios'" class="add-to-wallet" (click)="addCardToWallet($event)">Добавить в Apple Wallet</button>
|
||||
</div>
|
||||
@ -3,14 +3,18 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&>h2 {
|
||||
& > h2 {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&>p {
|
||||
& > p {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 15px;
|
||||
@ -19,7 +23,7 @@
|
||||
}
|
||||
|
||||
.card-container {
|
||||
background: #09467f;
|
||||
background: var(--main-color);
|
||||
box-shadow: 0px 0px 5px rgb(0 0 0 / 15%);
|
||||
border-radius: 15px;
|
||||
height: 466px;
|
||||
@ -67,7 +71,7 @@
|
||||
}
|
||||
|
||||
&__content {
|
||||
background-color: #09467f;
|
||||
background-color: var(--main-color);
|
||||
color: #fff;
|
||||
|
||||
.info {
|
||||
@ -154,12 +158,12 @@
|
||||
}
|
||||
|
||||
&.active_back {
|
||||
>.card-content__front {
|
||||
> .card-content__front {
|
||||
transform: rotateY(180deg);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
>.card-content__back {
|
||||
> .card-content__back {
|
||||
opacity: 1;
|
||||
transform: rotateY(0deg);
|
||||
}
|
||||
@ -196,5 +200,16 @@
|
||||
font-size: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.add-to-wallet {
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
background: var(--main-color);
|
||||
border-color: var(--main-color);
|
||||
color: #fff;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 12px auto 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,193 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import {
|
||||
orderStatuses,
|
||||
PageList,
|
||||
PageListWithBonus,
|
||||
} from 'src/app/app.constants';
|
||||
import {
|
||||
BonusProgramAccount,
|
||||
Page,
|
||||
Purchase,
|
||||
Transaction,
|
||||
} from 'src/app/interface/data';
|
||||
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
|
||||
import * as moment from 'moment-timezone';
|
||||
import * as barcode from 'jsbarcode';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { AppleWalletService } from 'src/app/services/apple-wallet.service';
|
||||
import { CookiesService } from 'src/app/services/cookies.service';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { MessageService } from 'primeng/api';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bonus-program',
|
||||
templateUrl: './bonus-program.component.html',
|
||||
styleUrls: ['./bonus-program.component.scss'],
|
||||
})
|
||||
export class BonusProgramComponent implements OnInit {
|
||||
public accountData!: BonusProgramAccount;
|
||||
public purchases: Purchase[] = [];
|
||||
public loadingBonuses: boolean = false;
|
||||
readonly orderStatuses = orderStatuses;
|
||||
readonly moment = moment;
|
||||
readonly pageList = environment.hasBonusProgram
|
||||
? PageListWithBonus
|
||||
: PageList;
|
||||
public currentPage: Page = this.pageList[1];
|
||||
public userName: string = '';
|
||||
public deviceType: 'ios' | 'android' | null = null;
|
||||
public bonuses: number = 0
|
||||
|
||||
constructor(
|
||||
private jsonrpc: JsonrpcService,
|
||||
private appleWallet: AppleWalletService,
|
||||
private cookiesService: CookiesService,
|
||||
@Inject(DOCUMENT) private document: Document,
|
||||
private http: HttpClient,
|
||||
private messageService: MessageService,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getAccountData();
|
||||
this.getTypeDevice();
|
||||
}
|
||||
|
||||
getTypeDevice() {
|
||||
const userAgent = window.navigator.userAgent.toLowerCase();
|
||||
const ios = /iphone|ipod|ipad/.test(userAgent);
|
||||
this.deviceType = ios ? 'ios' : 'android';
|
||||
}
|
||||
|
||||
async getAccountData(): Promise<void> {
|
||||
this.loadingBonuses = true;
|
||||
|
||||
this.jsonrpc
|
||||
.rpc(
|
||||
{
|
||||
method: 'getAdditionalInfo',
|
||||
params: [],
|
||||
},
|
||||
RpcService.authService,
|
||||
true
|
||||
)
|
||||
.subscribe({
|
||||
next: (res) => {
|
||||
this.userName = res?.data?.first_name;
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Error: ', err);
|
||||
},
|
||||
});
|
||||
|
||||
const getAccount = await lastValueFrom(
|
||||
this.jsonrpc.rpc(
|
||||
{
|
||||
method: 'GetAccounts',
|
||||
params: [],
|
||||
},
|
||||
RpcService.bonusService
|
||||
)
|
||||
);
|
||||
|
||||
this.accountData = getAccount && getAccount['Cards'][0];
|
||||
this.bonuses = this.accountData.BonusProgramAccounts?.reduce((previous, {Bonuses}) => {
|
||||
return previous + Bonuses
|
||||
}, 0) || this.accountData.Bonuses
|
||||
this.loadingBonuses = false;
|
||||
if (this.accountData) {
|
||||
barcode('#barcode')
|
||||
.options({ font: 'OCR-B' }) // Will affect all barcodes
|
||||
.EAN13(`${this.accountData.CardNumber}`.padStart(12, '0'), {
|
||||
fontSize: 18,
|
||||
textMargin: 0,
|
||||
})
|
||||
.render();
|
||||
}
|
||||
const getTransactions = (
|
||||
await lastValueFrom(
|
||||
this.jsonrpc.rpc(
|
||||
{
|
||||
method: 'GetAccountTransactions',
|
||||
params: [],
|
||||
},
|
||||
RpcService.bonusService
|
||||
)
|
||||
)
|
||||
)
|
||||
if (!getTransactions) {
|
||||
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
|
||||
return
|
||||
}
|
||||
const transactions: Transaction[] = getTransactions && getTransactions['Transactions'];
|
||||
|
||||
const getPurchases = (
|
||||
await lastValueFrom(
|
||||
this.jsonrpc.rpc(
|
||||
{
|
||||
method: 'GetAccountPurchase',
|
||||
params: [],
|
||||
},
|
||||
RpcService.bonusService
|
||||
)
|
||||
)
|
||||
)
|
||||
if (!getPurchases) {
|
||||
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
|
||||
return
|
||||
}
|
||||
const purchases: Purchase[] = getPurchases && getPurchases['Purchases'];
|
||||
|
||||
this.purchases = purchases && purchases.map<Purchase>((purchase) => {
|
||||
const id = purchase.ID.slice(0, 36).toLowerCase();
|
||||
purchase.Transactions = transactions.filter((transaction) => {
|
||||
const same = transaction.Purchase === id;
|
||||
transaction.HasPurchase = same;
|
||||
return same;
|
||||
});
|
||||
return purchase;
|
||||
});
|
||||
transactions && transactions.forEach((transaction) => {
|
||||
if (!transaction.HasPurchase) {
|
||||
this.purchases.push({
|
||||
ID: transaction.ID,
|
||||
PurchaseDate: transaction.Date,
|
||||
Transactions: [transaction],
|
||||
IsSingleTransaction: true,
|
||||
transactionSum: 0
|
||||
});
|
||||
}
|
||||
});
|
||||
this.purchases = this.purchases && this.purchases.sort((a, b) => {
|
||||
return moment(a.PurchaseDate).date() - moment(b.PurchaseDate).date();
|
||||
});
|
||||
}
|
||||
|
||||
async addCardToWallet(e: any) {
|
||||
e.preventDefault();
|
||||
const token = this.cookiesService.getItem('token');
|
||||
const accountData = (
|
||||
await lastValueFrom(
|
||||
this.jsonrpc.rpc(
|
||||
{
|
||||
method: 'getTokenData',
|
||||
params: [],
|
||||
},
|
||||
RpcService.authService,
|
||||
true
|
||||
)
|
||||
)
|
||||
).data;
|
||||
if (token && accountData.user_id) {
|
||||
this.appleWallet.generateCard(token, accountData.user_id).subscribe({
|
||||
next: (res: any) => {
|
||||
this.document.location.href = res.url;
|
||||
},
|
||||
error: (err) => {
|
||||
console.log('Error: ', err);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,7 +67,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<p
|
||||
*ngIf="purchases.length !== purchasesShortArray.length"
|
||||
*ngIf="purchases?.length !== purchasesShortArray?.length"
|
||||
class="show-more-orders"
|
||||
(click)="getMoreOrders()"
|
||||
>
|
||||
@ -75,12 +75,13 @@
|
||||
</p>
|
||||
<p-progressSpinner
|
||||
*ngIf="ordersLoadingStatus"
|
||||
[style]="{ width: '100%' }"
|
||||
[style]="{ display: 'block' }"
|
||||
strokeWidth="2"
|
||||
styleClass="angular-spinner"
|
||||
></p-progressSpinner>
|
||||
<p
|
||||
*ngIf="purchases.length === 0 && !ordersLoadingStatus"
|
||||
*ngIf="purchases?.length === 0 && !ordersLoadingStatus"
|
||||
class="no-orders"
|
||||
style="width: 100%; text-align: center"
|
||||
>
|
||||
Нет заказов
|
||||
76
angular/src/app/pages/account/orders/orders.component.scss
Normal file
76
angular/src/app/pages/account/orders/orders.component.scss
Normal file
@ -0,0 +1,76 @@
|
||||
.show-more-orders {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
color: var(--main-color);
|
||||
margin-top: 16px;
|
||||
}
|
||||
.woocommerce-MyAccount-content {
|
||||
max-width: 400px;
|
||||
margin-left: calc(50vw - 200px);
|
||||
}
|
||||
.woocommerce-orders-table {
|
||||
border: 2px solid #dee2e6;
|
||||
border-bottom: 0;
|
||||
border-radius: 0.25rem;
|
||||
border-collapse: separate;
|
||||
text-align: left;
|
||||
margin-top: 8px;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
thead {
|
||||
display: none;
|
||||
}
|
||||
.woocommerce-orders-table {
|
||||
&__cell {
|
||||
padding: 0.7rem 1.5rem;
|
||||
border-bottom: 2px solid #dee2e6;
|
||||
text-align: right !important;
|
||||
display: block;
|
||||
&::before {
|
||||
content: attr(data-title) ": ";
|
||||
font-weight: 700;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
&__row {
|
||||
display: block;
|
||||
&:nth-child(even) {
|
||||
background: #ebebeb;
|
||||
}
|
||||
}
|
||||
// &__cell-order-actions::before {
|
||||
// display: none;
|
||||
// }
|
||||
&__cell-order-actions {
|
||||
&.red-color {
|
||||
color: #ed0000;
|
||||
}
|
||||
&.green-color {
|
||||
color: #00a700;
|
||||
}
|
||||
}
|
||||
&__cell-order-number a {
|
||||
text-decoration: none;
|
||||
color: #009688;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-button {
|
||||
display: inline-block;
|
||||
color: #ffffff;
|
||||
background-color: #009688;
|
||||
padding: 13px;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.no-orders {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
@ -5,11 +5,12 @@ import { Purchase } from 'src/app/interface/data';
|
||||
import * as moment from 'moment-timezone';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
|
||||
import { MessageService } from 'primeng/api';
|
||||
|
||||
@Component({
|
||||
selector: 'app-orders',
|
||||
templateUrl: './orders.component.html',
|
||||
styleUrls: ['./orders.component.scss']
|
||||
styleUrls: ['./orders.component.scss'],
|
||||
})
|
||||
export class OrdersComponent implements OnInit {
|
||||
@Input() handleHttpError!: (error: HttpErrorResponse) => void;
|
||||
@ -23,24 +24,31 @@ export class OrdersComponent implements OnInit {
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private jsonrpc: JsonrpcService,
|
||||
) { }
|
||||
private messageService: MessageService,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getOrders()
|
||||
this.getOrders();
|
||||
}
|
||||
|
||||
async getOrders(){
|
||||
const purchases: Purchase[] = (await lastValueFrom(
|
||||
async getOrders() {
|
||||
const getPurchases = await lastValueFrom(
|
||||
this.jsonrpc.rpc(
|
||||
{
|
||||
method: 'GetAccountPurchase',
|
||||
params: []
|
||||
params: [],
|
||||
},
|
||||
RpcService.bonusService
|
||||
)))['Purchases'];
|
||||
)
|
||||
);
|
||||
if (!getPurchases) {
|
||||
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
|
||||
return
|
||||
}
|
||||
const purchases: Purchase[] = getPurchases && getPurchases['Purchases'];
|
||||
|
||||
this.purchases = purchases.map<Purchase>((purchase) => {
|
||||
const id = purchase.ID.slice(0,36).toLowerCase();
|
||||
this.purchases = purchases && purchases.map<Purchase>((purchase) => {
|
||||
const id = purchase.ID.slice(0, 36).toLowerCase();
|
||||
// purchase.Transactions = transactions.filter((transaction) => {
|
||||
// const same = transaction.Purchase === id;
|
||||
// transaction.HasPurchase = same;
|
||||
@ -48,7 +56,7 @@ export class OrdersComponent implements OnInit {
|
||||
// });
|
||||
return purchase;
|
||||
});
|
||||
this.purchasesShortArray = this.purchases.slice(0, this.lastViewOrder)
|
||||
this.purchasesShortArray = this.purchases && this.purchases.slice(0, this.lastViewOrder);
|
||||
this.ordersLoadingStatus = false;
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<div class="ref-system">
|
||||
<qrcode
|
||||
<!-- <qrcode
|
||||
*ngIf="!loading"
|
||||
[qrdata]="refUrl"
|
||||
[width]="256"
|
||||
[errorCorrectionLevel]="'M'"
|
||||
></qrcode>
|
||||
></qrcode> -->
|
||||
<p-progressSpinner
|
||||
*ngIf="loading"
|
||||
[style]="{ width: '100%' }"
|
||||
@ -8,23 +8,25 @@
|
||||
margin: 0 auto;
|
||||
.share {
|
||||
width: 50px;
|
||||
background-color: #09467f;
|
||||
background-color: var(--main-color);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
img {
|
||||
height: 26px;
|
||||
}
|
||||
}
|
||||
.copy {
|
||||
background-color: #09467f;
|
||||
background-color: var(--main-color);
|
||||
color: #fff;
|
||||
border-radius: 8px;
|
||||
width: calc(100% - 66px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,7 @@ import { environment } from 'src/environments/environment';
|
||||
styleUrls: ['./ref-system.component.scss']
|
||||
})
|
||||
export class RefSystemComponent implements OnInit {
|
||||
public refUrl: string = `${environment.defaultUrl}/?refCardNumber=`
|
||||
public refUrl: string = `${environment.defaultUrl}/?refUserId=`
|
||||
public loading: boolean = true;
|
||||
|
||||
constructor(
|
||||
@ -21,13 +21,18 @@ export class RefSystemComponent implements OnInit {
|
||||
|
||||
async ngOnInit() {
|
||||
const accountData = (await lastValueFrom(
|
||||
this.jsonrpc.rpc({
|
||||
method: 'GetAccounts',
|
||||
params: []
|
||||
},
|
||||
RpcService.bonusService
|
||||
)))['Cards'][0]
|
||||
this.refUrl += accountData.CardNumber
|
||||
this.jsonrpc
|
||||
.rpc(
|
||||
{
|
||||
method: 'getTokenData',
|
||||
params: [],
|
||||
},
|
||||
RpcService.authService,
|
||||
true
|
||||
)
|
||||
)).data
|
||||
|
||||
this.refUrl += accountData.user_id
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
@ -35,30 +40,32 @@ export class RefSystemComponent implements OnInit {
|
||||
if (navigator.share) {
|
||||
navigator.share({
|
||||
title: document.title,
|
||||
text: "Fashion Logica",
|
||||
text: "Coffee Like More",
|
||||
url: this.refUrl
|
||||
})
|
||||
.then(() => console.log('Successful share'))
|
||||
.catch((error) => {
|
||||
console.log('Error sharing:', error)
|
||||
});
|
||||
.then(() => console.log('Successful share'))
|
||||
.catch((error) => {
|
||||
console.log('Error sharing:', error)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
copyUrl() {
|
||||
navigator.clipboard.writeText(this.refUrl)
|
||||
.then(() => {
|
||||
this.messageService.add({
|
||||
severity: 'custom',
|
||||
summary: 'Ссылка скопирована!',
|
||||
.then(() => {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'custom',
|
||||
summary: 'Ссылка скопирована!',
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
this.messageService.clear();
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: 'Произошла ошибка!',
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: 'Произошла ошибка!',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -75,12 +75,12 @@
|
||||
<div class="decoration-pattern">
|
||||
<img
|
||||
style="height: 34px"
|
||||
src="./assets/card-decorative-pattern.svg"
|
||||
src="./assets/card-decorative-pattern.png"
|
||||
alt=""
|
||||
/>
|
||||
<img
|
||||
style="height: 16px; margin-left: 2px"
|
||||
src="./assets/card-decorative-pattern.svg"
|
||||
src="./assets/card-decorative-pattern.png"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
@ -186,4 +186,4 @@
|
||||
*/
|
||||
// input[type="date"]:focus::before,
|
||||
// input[type="date"]:valid::before { display: none }
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,9 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { UserDataForm } from 'src/app/interface/data';
|
||||
import { AppleWalletService } from 'src/app/services/apple-wallet.service';
|
||||
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
|
||||
|
||||
@Component({
|
||||
@ -21,6 +23,8 @@ export class UserDataComponent implements OnInit {
|
||||
constructor(
|
||||
private jsonRpcService: JsonrpcService,
|
||||
private messageService: MessageService,
|
||||
private appleWallet: AppleWalletService,
|
||||
private jsonrpc: JsonrpcService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -30,6 +34,14 @@ export class UserDataComponent implements OnInit {
|
||||
params: []
|
||||
}, RpcService.authService, true).subscribe({
|
||||
next: (res) => {
|
||||
if (res.code === 45) {
|
||||
this.messageService.add({severity:'warn', summary:'Данные не найдены, заполните их на этой странице'});
|
||||
return
|
||||
}
|
||||
if (!res.data) {
|
||||
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
|
||||
return
|
||||
}
|
||||
const { first_name, birthdate, gender } = res.data
|
||||
this.userData = { first_name, birthdate, gender }
|
||||
this.createUserDataForm({...this.userData})
|
||||
@ -59,8 +71,29 @@ export class UserDataComponent implements OnInit {
|
||||
method: 'updateAdditionalInfo',
|
||||
params: [this.userData]
|
||||
}, RpcService.authService, true).subscribe({
|
||||
next: () => {
|
||||
next: async () => {
|
||||
this.messageService.add({severity:'custom', summary:'Данные успешно обновлены!'});
|
||||
const accountData = (await lastValueFrom(
|
||||
this.jsonrpc
|
||||
.rpc(
|
||||
{
|
||||
method: 'getTokenData',
|
||||
params: [],
|
||||
},
|
||||
RpcService.authService,
|
||||
true
|
||||
)
|
||||
)).data
|
||||
if (accountData.user_id) {
|
||||
this.appleWallet.reloadCard(accountData.user_id).subscribe({
|
||||
next: (value) => {
|
||||
console.log(value);
|
||||
},
|
||||
error: (err) => {
|
||||
console.error(err);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Error: ', err)
|
||||
82
angular/src/app/pages/guest-card/guest-card.component.html
Normal file
82
angular/src/app/pages/guest-card/guest-card.component.html
Normal file
@ -0,0 +1,82 @@
|
||||
<app-navbar title="Карта гостя" (backEvent)="logout()"></app-navbar>
|
||||
|
||||
<div class="guest-card">
|
||||
<div class="top-info">
|
||||
<div class="top-info__level">
|
||||
<p id="level">Уровень {{ currentLvl }}</p>
|
||||
<p id="level-percent">Кэшбек {{ currentLvlPeriod.percent }}%</p>
|
||||
</div>
|
||||
<p class="top-info__bonus">{{ getBalanceAmount(customerInfo?.walletBalances) }} бонусов</p>
|
||||
</div>
|
||||
<div class="guest-card__qr" (click)="qrCodeClick()">
|
||||
<ng-container *ngIf="customerInfo">
|
||||
<qr-code
|
||||
[value]="customerInfo?.phone.substr(2) || 'Данные не найдены'"
|
||||
[margin]="0"
|
||||
[size]="qrCodeSize"
|
||||
errorCorrectionLevel="M"
|
||||
></qr-code>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!customerInfo">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="spinner; context: { $implicit: 85 }"
|
||||
></ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="guest-card__user-description">
|
||||
<p>
|
||||
<ng-container *ngIf="currentLvlPeriod.end">
|
||||
Осталось купить на сумму <span class="price">{{ currentLvlPeriod.end - (purchaseData.currentAmount || 0) + 1 }}</span>
|
||||
рублей, тогда кэшбек будет <span class="percent">{{ getNextLevel().percent }}%</span> с
|
||||
{{ purchaseData.currentPeriod[1].locale('ru').format('D MMMM')}}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!currentLvlPeriod.end">
|
||||
У Вас последний уровень бонусной программы. Процент начисляемых бонусов:
|
||||
{{currentLvlPeriod.percent}}%
|
||||
</ng-container>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<span id="bonuses-condition"></span>
|
||||
|
||||
<!-- <div class="guest-card__level-info"> -->
|
||||
<!-- <ng-container *ngIf="!purchaseData.$loading"> -->
|
||||
<!-- <ng-container *ngIf="currentLvlPeriod.end"> -->
|
||||
<!-- <\!-- <input -\-> -->
|
||||
<!-- <\!-- type="range" -\-> -->
|
||||
<!-- <\!-- [value]="purchaseData.currentAmount" -\-> -->
|
||||
<!-- <\!-- [min]="currentLvlPeriod.start" -\-> -->
|
||||
<!-- <\!-- [max]="currentLvlPeriod.end" -\-> -->
|
||||
<!-- <\!-- [step]="0.01" -\-> -->
|
||||
<!-- <\!-- disabled="true" -\-> -->
|
||||
<!-- <\!-- [ngStyle]="{ -\-> -->
|
||||
<!-- <\!-- 'background-size': ((purchaseData.currentAmount! - currentLvlPeriod.start) / (currentLvlPeriod.end - currentLvlPeriod.start + 1)) * 100 + '% 100%' -\-> -->
|
||||
<!-- <\!-- }" -\-> -->
|
||||
<!-- <\!-- /> -\-> -->
|
||||
<!-- </ng-container> -->
|
||||
<!-- <ng-container *ngIf="!currentLvlPeriod.end"> -->
|
||||
<!-- <h2> -->
|
||||
<!-- У Вас последний уровень бонусной программы. Процент начисляемых бонусов: {{currentLvlPeriod.percent}}% -->
|
||||
<!-- </h2> -->
|
||||
<!-- </ng-container> -->
|
||||
<!-- </ng-container> -->
|
||||
<!-- <ng-container *ngIf="purchaseData.$loading"> -->
|
||||
<!-- <ng-container -->
|
||||
<!-- *ngTemplateOutlet="spinner; context: { $implicit: 48 }" -->
|
||||
<!-- ></ng-container> -->
|
||||
<!-- </ng-container> -->
|
||||
|
||||
<!-- </div> -->
|
||||
<app-last-order
|
||||
[lastOrder]="lastPurchase"
|
||||
[loading]="purchaseData.$loading"
|
||||
></app-last-order>
|
||||
<a class="guest-card__loyalty-program" routerLink="loyality-program"
|
||||
>Подробнее о правилах <br />
|
||||
Программы лояльности</a
|
||||
>
|
||||
</div>
|
||||
|
||||
<ng-template #spinner let-diameter>
|
||||
<mat-spinner [strokeWidth]="3" [diameter]="diameter"></mat-spinner>
|
||||
</ng-template>
|
||||
221
angular/src/app/pages/guest-card/guest-card.component.scss
Normal file
221
angular/src/app/pages/guest-card/guest-card.component.scss
Normal file
@ -0,0 +1,221 @@
|
||||
:host {
|
||||
.guest-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
|
||||
.top-info {
|
||||
padding: 8px 0 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: #292929;
|
||||
border-bottom-left-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
border: 1px solid #000000;
|
||||
border-top: 1px solid #888888;
|
||||
|
||||
&__level {
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
& p {
|
||||
color: var(--button-color);
|
||||
}
|
||||
|
||||
p.top-info__bonus{
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__qr {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
width: fit-content;
|
||||
background-image: linear-gradient(
|
||||
#fff 33%,
|
||||
transparent 0px,
|
||||
transparent 67%,
|
||||
#fff 0px
|
||||
),
|
||||
linear-gradient(
|
||||
90deg,
|
||||
#ffe 33%,
|
||||
transparent 0px,
|
||||
transparent 66%,
|
||||
#fff 0px
|
||||
),
|
||||
linear-gradient(#fff 33%, transparent 0px, transparent 67%, #fff 0),
|
||||
linear-gradient(90deg, #fff 33%, transparent 0, transparent 66%, #fff 0);
|
||||
background-size: 1px 100%, 100% 1px, 1px 100%, 100% 1px;
|
||||
background-position: 0 0, 0 0, 100% 100%, 100% 100%;
|
||||
background-repeat: no-repeat, no-repeat, no-repeat, no-repeat;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__user-description {
|
||||
background-color: #292929;
|
||||
padding: 14px 24px;
|
||||
|
||||
p {
|
||||
width: 60%;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.price,
|
||||
.percent {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
&__purchases-description {
|
||||
margin: 0;
|
||||
padding: 14px 24px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
letter-spacing: -0.5px;
|
||||
grid-template-columns: calc(100% - 24px) 24px;
|
||||
|
||||
span {
|
||||
color: #219653;
|
||||
}
|
||||
}
|
||||
|
||||
&__level-info {
|
||||
padding-top: 36px;
|
||||
padding-left: 36px;
|
||||
padding-right: 36px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
h2 {
|
||||
font-style: normal;
|
||||
font-family: Montserrat;
|
||||
font-weight: 700;
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
letter-spacing: -0.408px;
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
-webkit-appearance: none;
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
position: relative;
|
||||
background: #231f20;
|
||||
box-shadow: 0px 0px 3px #f2c94c59;
|
||||
background-image: linear-gradient(#f2c94c, #f2c94c);
|
||||
background-size: 70% 100%;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 100%;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
background-color: #f2c94c;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
background-color: #f2c94c;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
background: #f2c94c;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 100%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::-ms-thumb {
|
||||
-webkit-appearance: none;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 50%;
|
||||
background: #f2c94c;
|
||||
cursor: ew-resize;
|
||||
box-shadow: 0 0 2px 0 #555;
|
||||
transition: background 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
-webkit-appearance: none;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 50%;
|
||||
background: #f2c94c;
|
||||
cursor: ew-resize;
|
||||
box-shadow: 0 0 2px 0 #555;
|
||||
transition: background 0.3s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__download-app {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
margin-top: 32px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
img {
|
||||
width: 100%;
|
||||
max-width: calc(100% - 16px);
|
||||
}
|
||||
}
|
||||
|
||||
&__loyalty-program {
|
||||
text-align: center;
|
||||
color: #7F2061;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
text-decoration: none;
|
||||
margin: 17px 0 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app-accordion {
|
||||
ul {
|
||||
li {
|
||||
list-style-type: disc;
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { GuestCardComponent } from './guest-card.component';
|
||||
|
||||
describe('GuestCardComponent', () => {
|
||||
let component: GuestCardComponent;
|
||||
let fixture: ComponentFixture<GuestCardComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ GuestCardComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(GuestCardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
193
angular/src/app/pages/guest-card/guest-card.component.ts
Normal file
193
angular/src/app/pages/guest-card/guest-card.component.ts
Normal file
@ -0,0 +1,193 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatBottomSheet } from '@angular/material/bottom-sheet';
|
||||
import { Router } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ExitComponent } from 'src/app/components/exit/exit.component';
|
||||
import { CookiesService } from 'src/app/services/cookies.service';
|
||||
import { WpJsonService } from 'src/app/services/wp-json.service';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import moment from 'moment';
|
||||
import { Purchase, lvlPeriod } from 'src/app/interface/data';
|
||||
import { lvlPeriods } from 'src/app/app.constants';
|
||||
|
||||
interface Moment extends moment.Moment {}
|
||||
|
||||
export interface IPurchaseData {
|
||||
currentPeriod: Moment[];
|
||||
lastPeriod: Moment[];
|
||||
lastPurchases: Purchase[];
|
||||
currentPurchases: Purchase[];
|
||||
lastAmount?: number;
|
||||
currentAmount?: number;
|
||||
$loading: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-guest-card',
|
||||
templateUrl: './guest-card.component.html',
|
||||
styleUrls: ['./guest-card.component.scss'],
|
||||
})
|
||||
export class GuestCardComponent implements OnInit {
|
||||
public qrCodeSize: number = 85;
|
||||
private isQrCodeClicked: boolean = false;
|
||||
public customerInfo!: any;
|
||||
public purchases!: Purchase[];
|
||||
public lastPurchase!: Purchase;
|
||||
public purchaseData: IPurchaseData = {
|
||||
currentPeriod: [],
|
||||
lastPeriod: [],
|
||||
lastPurchases: [],
|
||||
currentPurchases: [],
|
||||
$loading: true,
|
||||
get currentAmount():number {
|
||||
const amount = this.currentPurchases.reduce((accumulator, currentValue) => {
|
||||
if (currentValue.transactionType !== 'PayFromWallet') return accumulator;
|
||||
return accumulator + currentValue.orderSum!;
|
||||
}, 0);
|
||||
return amount * 1
|
||||
},
|
||||
get lastAmount():number {
|
||||
const amount = this.lastPurchases.reduce((accumulator, currentValue) => {
|
||||
if (currentValue.transactionType !== 'PayFromWallet') return accumulator;
|
||||
return accumulator + currentValue.orderSum!;
|
||||
}, 0);
|
||||
return amount * 1
|
||||
}
|
||||
};
|
||||
|
||||
public discountLevel: number = 4.2;
|
||||
public lvlPeriods: lvlPeriod[] = lvlPeriods;
|
||||
public currentLvlPeriod!: lvlPeriod;
|
||||
public currentLvl: number = 1;
|
||||
|
||||
constructor(
|
||||
private _bottomSheet: MatBottomSheet,
|
||||
private cookiesService: CookiesService,
|
||||
private router: Router,
|
||||
private wpJsonService: WpJsonService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
const token = this.cookiesService.getItem('token');
|
||||
this.getCurrentQuarterOfYear();
|
||||
this.wpJsonService
|
||||
.getCustomerInfo(
|
||||
environment.systemId,
|
||||
token || '',
|
||||
environment.icardProxy
|
||||
)
|
||||
.subscribe({
|
||||
next: (value) => {
|
||||
this.customerInfo = value.customer_info;
|
||||
this.getPurchases().subscribe((value) => {
|
||||
this.purchases = value[this.customerInfo?.id].filter(
|
||||
(purchase: Purchase) =>
|
||||
[
|
||||
'PayFromWallet',
|
||||
'CancelPayFromWallet',
|
||||
'RefillWallet',
|
||||
'RefillWalletFromOrder'
|
||||
].includes(purchase.transactionType || '')
|
||||
);
|
||||
this.lastPurchase = this.purchases.filter(
|
||||
(purchase: Purchase) =>
|
||||
[
|
||||
'PayFromWallet',
|
||||
'RefillWalletFromOrder'
|
||||
].includes(purchase.transactionType || '')
|
||||
)[0]
|
||||
|
||||
this.purchaseData.lastPurchases = this.purchases.filter((value: Purchase) => {
|
||||
return moment(value.transactionCreateDate).isBetween(this.purchaseData.lastPeriod[0], this.purchaseData.lastPeriod[1])
|
||||
})
|
||||
this.purchaseData.currentPurchases = this.purchases.filter((value: Purchase) => {
|
||||
return moment(value.transactionCreateDate).isBetween(this.purchaseData.currentPeriod[0], this.purchaseData.currentPeriod[1])
|
||||
})
|
||||
const currentAmount = this.purchaseData.currentAmount || 0
|
||||
const index = this.lvlPeriods.findIndex((item) => item.start <= currentAmount && currentAmount <= (item.end || Infinity))!
|
||||
if(index != -1) {
|
||||
this.currentLvlPeriod = this.lvlPeriods[index];
|
||||
this.currentLvl = index + 1;
|
||||
this.purchaseData.$loading = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getNextLevel(): lvlPeriod {
|
||||
if(this.currentLvl == this.lvlPeriods.length) {
|
||||
return lvlPeriods[this.currentLvl - 1];
|
||||
}
|
||||
return lvlPeriods[this.currentLvl];
|
||||
}
|
||||
|
||||
qrCodeClick() {
|
||||
this.isQrCodeClicked = !this.isQrCodeClicked;
|
||||
this.qrCodeSize = this.isQrCodeClicked ? 180 : 85;
|
||||
}
|
||||
|
||||
deleteToken(): void {
|
||||
this.cookiesService.logout();
|
||||
}
|
||||
|
||||
logout() {
|
||||
const bottomSheet = this._bottomSheet.open(ExitComponent);
|
||||
bottomSheet.afterDismissed().subscribe({
|
||||
next: (val) => {
|
||||
if (val) {
|
||||
this.deleteToken();
|
||||
this.router.navigate(['/login']);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getPurchases(
|
||||
start: Date | Moment = moment().subtract(1, 'months').startOf('month'),
|
||||
end: Date | Moment = moment()
|
||||
): Observable<any> {
|
||||
const token = this.cookiesService.getItem('token');
|
||||
const delta = moment(end).diff(moment(start), 'days');
|
||||
return this.wpJsonService.getTransactions(
|
||||
environment.systemId,
|
||||
token ?? '',
|
||||
environment.icardProxy
|
||||
);
|
||||
}
|
||||
|
||||
getCurrentQuarterOfYear() {
|
||||
const quarters = [
|
||||
[
|
||||
moment().subtract(1, 'years').endOf('year').subtract(3, 'months'),
|
||||
moment().subtract(1, 'years').endOf('year'),
|
||||
],
|
||||
[moment().startOf('year'), moment().startOf('year').add(3, 'months')],
|
||||
[
|
||||
moment().startOf('year').add(3, 'months'),
|
||||
moment().startOf('year').add(6, 'months'),
|
||||
],
|
||||
[
|
||||
moment().startOf('year').add(6, 'months'),
|
||||
moment().startOf('year').add(9, 'months'),
|
||||
],
|
||||
[
|
||||
moment().startOf('year').add(9, 'months'),
|
||||
moment().startOf('year').add(12, 'months'),
|
||||
],
|
||||
];
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (moment().isBetween(quarters[i][0], quarters[i][1])) {
|
||||
this.purchaseData.lastPeriod = quarters[i - 1];
|
||||
this.purchaseData.currentPeriod = quarters[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getBalanceAmount(loyaltyPrograms: any[]) {
|
||||
return loyaltyPrograms.reduce((accumulator, currentValue) => {
|
||||
return accumulator + currentValue.balance;
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
107
angular/src/app/pages/login/login.component.html
Normal file
107
angular/src/app/pages/login/login.component.html
Normal file
@ -0,0 +1,107 @@
|
||||
<app-navbar
|
||||
*ngIf="phoneForm.value.phone && !isShowNumber"
|
||||
[title]="phoneForm.value.phone"
|
||||
(backEvent)="backToPhoneForm()"
|
||||
[ngStyle]="{
|
||||
position: 'absolute',
|
||||
top: 0
|
||||
}"
|
||||
></app-navbar>
|
||||
<h1>Участвуй в программе лояльности COFFEE LIKE</h1>
|
||||
<p class="description">Начни получать бонусы прямо сейчас</p>
|
||||
<form
|
||||
*ngIf="isShowNumber; else smsCode"
|
||||
(ngSubmit)="submitNumber()"
|
||||
[formGroup]="phoneForm"
|
||||
>
|
||||
<!-- <div class="input-container"> -->
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Ваше имя</mat-label>
|
||||
<input formControlName="name" matInput type="text" />
|
||||
</mat-form-field>
|
||||
<!-- </div> -->
|
||||
<div class="input-container">
|
||||
<!-- <label for="number">Номер телефона</label>
|
||||
<input id="number" type="text" placeholder="Введите номер" /> -->
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Номер телефона</mat-label>
|
||||
<ngx-mat-intl-tel-input
|
||||
formControlName="phone"
|
||||
[enablePlaceholder]="true"
|
||||
[enableSearch]="false"
|
||||
[onlyCountries]="['ru', 'kg', 'by', 'kz', 'fi', 'de']"
|
||||
[preferredCountries]="['ru']"
|
||||
name="phone"
|
||||
#phone
|
||||
>
|
||||
</ngx-mat-intl-tel-input>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<p class="offer">
|
||||
Используя приложение, вы принимаете условия в <span>соглашениях</span> и
|
||||
соглашаетесь на получение рекламно-информационных сообщений
|
||||
</p>
|
||||
<button [disabled]="phoneForm.invalid">Принять участие</button>
|
||||
</form>
|
||||
|
||||
<ng-template #smsCode>
|
||||
<h2>Введите код из SMS</h2>
|
||||
<form class="code-form" [formGroup]="codeForm" (ngSubmit)="submitCode()">
|
||||
<div class="inputs-container">
|
||||
<label class="box"
|
||||
><input
|
||||
class="field"
|
||||
id="field"
|
||||
type="tel"
|
||||
placeholder="•"
|
||||
#field
|
||||
[appFocusNextInput]="inputFocusEmitter"
|
||||
formControlName="code"
|
||||
maxlength="1"
|
||||
/></label>
|
||||
<label class="box"
|
||||
><input
|
||||
class="field"
|
||||
id="field1"
|
||||
type="tel"
|
||||
placeholder="•"
|
||||
#field1
|
||||
[appFocusNextInput]="inputFocusEmitter"
|
||||
formControlName="code1"
|
||||
maxlength="1"
|
||||
/></label>
|
||||
<label class="box"
|
||||
><input
|
||||
class="field"
|
||||
id="field2"
|
||||
type="tel"
|
||||
placeholder="•"
|
||||
#field2
|
||||
[appFocusNextInput]="inputFocusEmitter"
|
||||
formControlName="code2"
|
||||
maxlength="1"
|
||||
/></label>
|
||||
<label class="box"
|
||||
><input
|
||||
class="field"
|
||||
id="field3"
|
||||
type="tel"
|
||||
placeholder="•"
|
||||
#field3
|
||||
[appFocusNextInput]="inputFocusEmitter"
|
||||
formControlName="code3"
|
||||
maxlength="1"
|
||||
/></label>
|
||||
</div>
|
||||
<button>Войти</button>
|
||||
</form>
|
||||
<p class="resend-code">
|
||||
Не пришло SMS?<br />
|
||||
<ng-container *ngIf="timeLeft">
|
||||
Отправим повторно через {{timeLeft}}с
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!timeLeft">
|
||||
<span class="resend" (click)="submitNumber()">Отправить повторно</span>
|
||||
</ng-container>
|
||||
</p>
|
||||
</ng-template>
|
||||
214
angular/src/app/pages/login/login.component.scss
Normal file
214
angular/src/app/pages/login/login.component.scss
Normal file
@ -0,0 +1,214 @@
|
||||
:host {
|
||||
padding-top: 48px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
max-width: 600px;
|
||||
margin: 0 auto 52px;
|
||||
|
||||
h1 {
|
||||
margin-top: 20px;
|
||||
width: 302px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
letter-spacing: -0.408px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 44px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
letter-spacing: 0.38px;
|
||||
}
|
||||
|
||||
.description {
|
||||
width: 180px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
letter-spacing: -0.24px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 35px;
|
||||
|
||||
.offer {
|
||||
margin-top: 10px;
|
||||
padding: 0 16px;
|
||||
font-family: "Gowun Dodum";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 17px;
|
||||
text-align: center;
|
||||
span {
|
||||
color: #13a538;
|
||||
}
|
||||
}
|
||||
|
||||
.input-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
label {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: #6a737c;
|
||||
text-align: left;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 16px;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 24px 16px 8px;
|
||||
background-color: #252323;
|
||||
margin-bottom: 16px;
|
||||
border: none;
|
||||
border-top: solid #6a737c 1px;
|
||||
border-bottom: solid #6a737c 1px;
|
||||
color: #6a737c;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 22px;
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
height: 46px;
|
||||
width: calc(100% - 32px);
|
||||
padding: 10px 24px;
|
||||
background: #28af49;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: none;
|
||||
letter-spacing: -0.408px;
|
||||
color: #ffffff;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
&:disabled {
|
||||
background-color: #344a3a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.code-form {
|
||||
width: 100%;
|
||||
// input {
|
||||
// width: 100%;
|
||||
// padding: 24px 16px 8px;
|
||||
// background-color: #252323;
|
||||
// margin-bottom: 16px;
|
||||
// border: none;
|
||||
// border-top: solid #6a737c 1px;
|
||||
// border-bottom: solid #6a737c 1px;
|
||||
// color: #6a737c;
|
||||
// font-style: normal;
|
||||
// font-weight: 400;
|
||||
// font-size: 22px;
|
||||
// line-height: 28px;
|
||||
// }
|
||||
.inputs-container {
|
||||
width: 102px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 40px;
|
||||
|
||||
// code
|
||||
.box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 42px;
|
||||
width: 42px;
|
||||
border-radius: 6px;
|
||||
// box-shadow: 0 0 6px 1px hsla(240, 54%, 61%, 0.2);
|
||||
overflow: hidden;
|
||||
will-change: transform;
|
||||
}
|
||||
.box:focus-within {
|
||||
box-shadow: 0 0 6px 1px rgba($color: #28af49, $alpha: 0.2),
|
||||
0 0 0 2px rgba($color: #28af49, $alpha: 0.6);
|
||||
}
|
||||
.box::before,
|
||||
.box::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.box::before {
|
||||
// background: hsl(240, 54%, 97%);
|
||||
z-index: 1;
|
||||
transition: background-color 450ms cubic-bezier(0.25, 0.01, 0.25, 1);
|
||||
}
|
||||
.box::after {
|
||||
transform: translateY(100%);
|
||||
background-color: hsl(145, 0%, 42%);
|
||||
opacity: 0;
|
||||
z-index: 10;
|
||||
transition: transform 450ms cubic-bezier(0.25, 0.01, 0.25, 1),
|
||||
opacity 450ms cubic-bezier(0.25, 0.01, 0.25, 1),
|
||||
background-color 450ms cubic-bezier(0.25, 0.01, 0.25, 1);
|
||||
}
|
||||
|
||||
.field {
|
||||
position: relative;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
font-size: 25.21px;
|
||||
line-height: 42px;
|
||||
color: #fff;
|
||||
background-color: transparent;
|
||||
text-align: center;
|
||||
z-index: 100;
|
||||
}
|
||||
.field::placeholder {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.resend-code {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
margin-top: 23px;
|
||||
.resend {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
}
|
||||
23
angular/src/app/pages/login/login.component.spec.ts
Normal file
23
angular/src/app/pages/login/login.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoginComponent } from './login.component';
|
||||
|
||||
describe('LoginComponent', () => {
|
||||
let component: LoginComponent;
|
||||
let fixture: ComponentFixture<LoginComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ LoginComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LoginComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user