Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 70a4d5713b | |||
| 22858a2f49 | |||
| cc5b1e4ba9 | |||
| ac7709b64e | |||
| 2428870ba8 | |||
| ab8e909b08 | |||
| 673afcf2b1 | |||
| a6c0f98ec6 | |||
| 7f96fa4503 | |||
| 1a503b3163 | |||
| 00a47756b2 | |||
| 10ad10d922 | |||
| ed2e73e598 | |||
| ced1f14cff | |||
| c24e94c255 | |||
| c282a6e521 | |||
|
|
9e7b91e117 | ||
|
|
624d121006 | ||
| ce61d8b9aa | |||
| 5b0516f37e | |||
| 1960c6a7d3 | |||
|
|
26aa47b004 | ||
| e7a389b484 | |||
| df49c2a77e | |||
| 2e3da3d0b2 | |||
| 6344de6931 | |||
| 72e308c706 | |||
| 30edd5d8c1 | |||
| 7b61806984 | |||
| f6ffa897bd | |||
| 661a5b700a | |||
| 88a88e7541 | |||
| 3040cef0b8 | |||
| 7fe1d8c22e | |||
| 6dad05b628 | |||
| 0beee8c5f7 | |||
| ea63515f74 | |||
| 4c614ff492 | |||
| 6e05417f3b | |||
| 186351984f | |||
| 7a12e6d4f7 | |||
| c7db59110e | |||
| 801f33623b | |||
| 63fac5b48d | |||
|
|
787e08c7be | ||
| 3120528bb2 | |||
| 5d5a04c00f | |||
| 1cd4cc1201 | |||
| 1cebb09849 | |||
| 266601e59b | |||
| 5a5acb53fd | |||
| 352986de93 | |||
| d9836f2b40 | |||
| aea275e705 | |||
| b5fd880379 | |||
| 694dbbfb8d | |||
| 0adafa4d1c | |||
| ed41ab6627 | |||
|
|
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 |
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
33
Jenkinsfile
vendored
Normal file
33
Jenkinsfile
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
env.HL_BUILD_MODE = "jenkins"
|
||||
|
||||
node('Lithium'){
|
||||
|
||||
stage('get new version to repo') {
|
||||
checkout scm
|
||||
if (lastCommitIsBumpCommit()) {
|
||||
currentBuild.result = 'ABORTED'
|
||||
error('Последний коммит - результат сборки jenkins')
|
||||
}
|
||||
sh "git checkout ${env.BRANCH_NAME}"
|
||||
sh "git checkout -- ."
|
||||
|
||||
sh "git pull"
|
||||
//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
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
# FashionLogica
|
||||
# Sakura
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.0.6.
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
{
|
||||
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"fashion-logica": {
|
||||
"sakura": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
},
|
||||
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
@ -17,8 +19,9 @@
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "/var/www/lk/fashion-logica",
|
||||
"baseHref": "/fashion-logica/",
|
||||
"outputPath": "/var/www/html/lk.crm4retail.ru/sakura",
|
||||
"deleteOutputPath": false,
|
||||
"baseHref": "/",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
@ -31,7 +34,9 @@
|
||||
"src/firebase-messaging-sw.js"
|
||||
],
|
||||
"styles": [
|
||||
"node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css",
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
"node_modules/mdb-angular-ui-kit/assets/scss/mdb.scss",
|
||||
"node_modules/primeng/resources/themes/saga-blue/theme.css",
|
||||
"node_modules/primeicons/primeicons.css",
|
||||
"node_modules/primeng/resources/primeng.min.css",
|
||||
"node_modules/ngx-sharebuttons/themes/modern.scss",
|
||||
@ -53,7 +58,7 @@
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
"maximumError": "4.5kb"
|
||||
}
|
||||
],
|
||||
"fileReplacements": [
|
||||
@ -79,10 +84,11 @@
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "fashion-logica:build:production"
|
||||
"browserTarget": "sakura:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "fashion-logica:build:development"
|
||||
"browserTarget": "sakura:build:development",
|
||||
"proxyConfig": "proxy.confi.json"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
@ -90,7 +96,7 @@
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "fashion-logica:build"
|
||||
"browserTarget": "sakura:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
@ -108,6 +114,7 @@
|
||||
"src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
129
package-lock.json → angular/package-lock.json
generated
129
package-lock.json → angular/package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "fashion-logica",
|
||||
"version": "0.0.0",
|
||||
"name": "sakura",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "fashion-logica",
|
||||
"version": "0.0.0",
|
||||
"name": "sakura",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@angular/animations": "^14.0.0",
|
||||
"@angular/cdk": "^14.2.1",
|
||||
@ -15,14 +15,18 @@
|
||||
"@angular/core": "^14.0.0",
|
||||
"@angular/fire": "^7.4.1",
|
||||
"@angular/forms": "^14.0.0",
|
||||
"@angular/material": "^14.2.1",
|
||||
"@angular/platform-browser": "^14.0.0",
|
||||
"@angular/platform-browser-dynamic": "^14.0.0",
|
||||
"@angular/router": "^14.0.0",
|
||||
"@angular/service-worker": "^14.0.0",
|
||||
"@fortawesome/angular-fontawesome": "^0.11.1",
|
||||
"@fortawesome/fontawesome-free": "^6.0.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.2.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.2.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.0",
|
||||
"@ngrx/effects": "^14.3.2",
|
||||
"@ngrx/store": "^14.3.2",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"angular-moment-timezone": "^1.7.1",
|
||||
"angularx-qrcode": "^14.0.0",
|
||||
@ -30,6 +34,7 @@
|
||||
"firebase": "^9.9.3",
|
||||
"google-libphonenumber": "^3.2.30",
|
||||
"jsbarcode": "^3.11.5",
|
||||
"mdb-angular-ui-kit": "^3.0.0",
|
||||
"ngx-sharebuttons": "^11.0.0",
|
||||
"primeicons": "^5.0.0",
|
||||
"primeng": "^14.0.1",
|
||||
@ -44,6 +49,7 @@
|
||||
"@angular/compiler-cli": "^14.0.0",
|
||||
"@types/google-libphonenumber": "^7.4.23",
|
||||
"@types/jasmine": "~4.0.0",
|
||||
"@types/lodash": "^4.14.186",
|
||||
"jasmine-core": "~4.1.0",
|
||||
"karma": "~6.3.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
@ -665,6 +671,23 @@
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/material": {
|
||||
"version": "14.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular/material/-/material-14.2.1.tgz",
|
||||
"integrity": "sha512-e7DkKJTuqrSpKPhxahrqkZt6AeU5ld5/aSeCamq2dcdqfZ8otmgiajzN0cXZGwSCT2Lth6c+QV3yn8ufTJQpTw==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/animations": "^14.0.0 || ^15.0.0",
|
||||
"@angular/cdk": "14.2.1",
|
||||
"@angular/common": "^14.0.0 || ^15.0.0",
|
||||
"@angular/core": "^14.0.0 || ^15.0.0",
|
||||
"@angular/forms": "^14.0.0 || ^15.0.0",
|
||||
"@angular/platform-browser": "^14.0.0 || ^15.0.0",
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/platform-browser": {
|
||||
"version": "14.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.1.3.tgz",
|
||||
@ -3225,6 +3248,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-free": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.2.1.tgz",
|
||||
"integrity": "sha512-viouXhegu/TjkvYQoiRZK3aax69dGXxgEjpvZW81wIJdxm5Fnvp3VVIP4VHKqX4SvFw6qpmkILkD4RJWAdrt7A==",
|
||||
"hasInstallScript": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-svg-core": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.2.0.tgz",
|
||||
@ -3490,6 +3522,31 @@
|
||||
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@ngrx/effects": {
|
||||
"version": "14.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-14.3.2.tgz",
|
||||
"integrity": "sha512-6bpGfA44jzwhBcmNaTwVgnFmYOX9iKPFpXyetDe41tVESo1CsNhUBPTmISDXKN9Mx2mwGbsMxrn6QFRypSsKAQ==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "^14.0.0",
|
||||
"@ngrx/store": "14.3.2",
|
||||
"rxjs": "^6.5.3 || ^7.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ngrx/store": {
|
||||
"version": "14.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@ngrx/store/-/store-14.3.2.tgz",
|
||||
"integrity": "sha512-XGHjr0arh6gClo8Ce+xqJLvW9PkeXPW2tCo9Z5qMtHFI/z5dUppLVKGmMgD/fQBDyoqWTK5xu+89tDkdeZfRjQ==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "^14.0.0",
|
||||
"rxjs": "^6.5.3 || ^7.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ngtools/webpack": {
|
||||
"version": "14.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.1.3.tgz",
|
||||
@ -3914,6 +3971,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
|
||||
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.14.186",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.186.tgz",
|
||||
"integrity": "sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/long": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
|
||||
@ -9241,6 +9304,21 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/mdb-angular-ui-kit": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdb-angular-ui-kit/-/mdb-angular-ui-kit-3.0.0.tgz",
|
||||
"integrity": "sha512-D1gP9pC6CwYHg1O6WJeXTr6k78FNU/A55Gsez5ICTj6rM2sn3A3FoxrIYz8OGgaAVhm5NjBxX7N+2R0EF7dVwQ==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/animations": "^14.0.0",
|
||||
"@angular/cdk": "^14.0.0",
|
||||
"@angular/common": "^14.0.0",
|
||||
"@angular/core": "^14.0.0",
|
||||
"@angular/forms": "^14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@ -14157,6 +14235,14 @@
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"@angular/material": {
|
||||
"version": "14.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular/material/-/material-14.2.1.tgz",
|
||||
"integrity": "sha512-e7DkKJTuqrSpKPhxahrqkZt6AeU5ld5/aSeCamq2dcdqfZ8otmgiajzN0cXZGwSCT2Lth6c+QV3yn8ufTJQpTw==",
|
||||
"requires": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"@angular/platform-browser": {
|
||||
"version": "14.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.1.3.tgz",
|
||||
@ -15956,6 +16042,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.2.0.tgz",
|
||||
"integrity": "sha512-rBevIsj2nclStJ7AxTdfsa3ovHb1H+qApwrxcTVo+NNdeJiB9V75hsKfrkG5AwNcRUNxrPPiScGYCNmLMoh8pg=="
|
||||
},
|
||||
"@fortawesome/fontawesome-free": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.2.1.tgz",
|
||||
"integrity": "sha512-viouXhegu/TjkvYQoiRZK3aax69dGXxgEjpvZW81wIJdxm5Fnvp3VVIP4VHKqX4SvFw6qpmkILkD4RJWAdrt7A=="
|
||||
},
|
||||
"@fortawesome/fontawesome-svg-core": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.2.0.tgz",
|
||||
@ -16168,6 +16259,22 @@
|
||||
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==",
|
||||
"dev": true
|
||||
},
|
||||
"@ngrx/effects": {
|
||||
"version": "14.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-14.3.2.tgz",
|
||||
"integrity": "sha512-6bpGfA44jzwhBcmNaTwVgnFmYOX9iKPFpXyetDe41tVESo1CsNhUBPTmISDXKN9Mx2mwGbsMxrn6QFRypSsKAQ==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@ngrx/store": {
|
||||
"version": "14.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@ngrx/store/-/store-14.3.2.tgz",
|
||||
"integrity": "sha512-XGHjr0arh6gClo8Ce+xqJLvW9PkeXPW2tCo9Z5qMtHFI/z5dUppLVKGmMgD/fQBDyoqWTK5xu+89tDkdeZfRjQ==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@ngtools/webpack": {
|
||||
"version": "14.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.1.3.tgz",
|
||||
@ -16521,6 +16628,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
|
||||
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.186",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.186.tgz",
|
||||
"integrity": "sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/long": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
|
||||
@ -20455,6 +20568,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mdb-angular-ui-kit": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdb-angular-ui-kit/-/mdb-angular-ui-kit-3.0.0.tgz",
|
||||
"integrity": "sha512-D1gP9pC6CwYHg1O6WJeXTr6k78FNU/A55Gsez5ICTj6rM2sn3A3FoxrIYz8OGgaAVhm5NjBxX7N+2R0EF7dVwQ==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "fashion-logica",
|
||||
"name": "sakura",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve --host 192.168.0.179",
|
||||
"start": "ng serve --host 192.168.0.14 --verbose",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
@ -17,14 +17,18 @@
|
||||
"@angular/core": "^14.0.0",
|
||||
"@angular/fire": "^7.4.1",
|
||||
"@angular/forms": "^14.0.0",
|
||||
"@angular/material": "^14.2.1",
|
||||
"@angular/platform-browser": "^14.0.0",
|
||||
"@angular/platform-browser-dynamic": "^14.0.0",
|
||||
"@angular/router": "^14.0.0",
|
||||
"@angular/service-worker": "^14.0.0",
|
||||
"@fortawesome/angular-fontawesome": "^0.11.1",
|
||||
"@fortawesome/fontawesome-free": "^6.0.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.2.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.2.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.0",
|
||||
"@ngrx/effects": "^14.3.2",
|
||||
"@ngrx/store": "^14.3.2",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"angular-moment-timezone": "^1.7.1",
|
||||
"angularx-qrcode": "^14.0.0",
|
||||
@ -32,6 +36,7 @@
|
||||
"firebase": "^9.9.3",
|
||||
"google-libphonenumber": "^3.2.30",
|
||||
"jsbarcode": "^3.11.5",
|
||||
"mdb-angular-ui-kit": "^3.0.0",
|
||||
"ngx-sharebuttons": "^11.0.0",
|
||||
"primeicons": "^5.0.0",
|
||||
"primeng": "^14.0.1",
|
||||
@ -46,6 +51,7 @@
|
||||
"@angular/compiler-cli": "^14.0.0",
|
||||
"@types/google-libphonenumber": "^7.4.23",
|
||||
"@types/jasmine": "~4.0.0",
|
||||
"@types/lodash": "^4.14.186",
|
||||
"jasmine-core": "~4.1.0",
|
||||
"karma": "~6.3.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
@ -54,4 +60,4 @@
|
||||
"karma-jasmine-html-reporter": "~1.7.0",
|
||||
"typescript": "~4.7.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
29
angular/proxy.confi.json
Normal file
29
angular/proxy.confi.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"/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"
|
||||
},
|
||||
"/api/orders": {
|
||||
"target": "https://sakura.lk.crm4retail.ru/api/orders",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/api/orders": ""
|
||||
},
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
}
|
||||
}
|
||||
5
angular/src/app/app.component.html
Normal file
5
angular/src/app/app.component.html
Normal file
@ -0,0 +1,5 @@
|
||||
<app-navbar></app-navbar>
|
||||
<p-toast position="top-center"></p-toast>
|
||||
<div class="layout">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
5
angular/src/app/app.component.scss
Normal file
5
angular/src/app/app.component.scss
Normal file
@ -0,0 +1,5 @@
|
||||
:host {
|
||||
.layout {
|
||||
padding: 0 16px 16px;
|
||||
}
|
||||
}
|
||||
@ -20,16 +20,16 @@ describe('AppComponent', () => {
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'fashion-logica'`, () => {
|
||||
it(`should have as title 'sakura'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title).toEqual('fashion-logica');
|
||||
expect(app.title).toEqual('sakura');
|
||||
});
|
||||
|
||||
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('sakura app is running!');
|
||||
});
|
||||
});
|
||||
22
angular/src/app/app.component.ts
Normal file
22
angular/src/app/app.component.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { PrimeNGConfig } from 'primeng/api';
|
||||
import * as ConfigActions from './state/config/config.actions';
|
||||
import * as ProfileActions from './state/profile/profile.actions';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
title = 'Sakura';
|
||||
|
||||
constructor(private primengConfig: PrimeNGConfig, private store: Store) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.primengConfig.ripple = false;
|
||||
this.store.dispatch(ConfigActions.getConfig());
|
||||
this.store.dispatch(ProfileActions.getProfile());
|
||||
}
|
||||
}
|
||||
158
angular/src/app/app.constants.ts
Normal file
158
angular/src/app/app.constants.ts
Normal file
@ -0,0 +1,158 @@
|
||||
import { MatDateFormats } from '@angular/material/core';
|
||||
import {
|
||||
IOptionDateFilter,
|
||||
MainPageCode,
|
||||
OrderStatus,
|
||||
Page,
|
||||
PageCode,
|
||||
PaymentMethod,
|
||||
} 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: false,
|
||||
},
|
||||
{
|
||||
code: PageCode.Transactions,
|
||||
name: 'Чеки',
|
||||
description: '',
|
||||
resName: 'transactions',
|
||||
onSideBar: true,
|
||||
},
|
||||
{
|
||||
code: PageCode.Orders,
|
||||
name: 'Заказы',
|
||||
description: '',
|
||||
resName: 'orders',
|
||||
onSideBar: true,
|
||||
},
|
||||
{
|
||||
code: PageCode.Logout,
|
||||
name: 'Выйти',
|
||||
description: '',
|
||||
resName: 'logout',
|
||||
onSideBar: true,
|
||||
},
|
||||
// {
|
||||
// code: PageCode.UserData,
|
||||
// name: 'Заполнить анкету',
|
||||
// description: '',
|
||||
// resName: 'user-data',
|
||||
// onSideBar: true
|
||||
// },
|
||||
// {
|
||||
// code: PageCode.RefSystem,
|
||||
// name: 'Пригласить друга',
|
||||
// description: '',
|
||||
// resName: 'ref-system',
|
||||
// onSideBar: true,
|
||||
// },
|
||||
];
|
||||
|
||||
export const PageListMain: Page[] = [
|
||||
{
|
||||
code: MainPageCode.Account,
|
||||
name: 'Аккаунт',
|
||||
resName: 'account',
|
||||
onSideBar: true,
|
||||
icon: 'person',
|
||||
},
|
||||
{
|
||||
code: MainPageCode.Products,
|
||||
name: 'Товары',
|
||||
resName: 'products',
|
||||
onSideBar: true,
|
||||
icon: 'manage_search',
|
||||
},
|
||||
{
|
||||
code: MainPageCode.Cart,
|
||||
name: 'Корзина',
|
||||
resName: 'cart',
|
||||
onSideBar: true,
|
||||
icon: 'shopping_bag',
|
||||
},
|
||||
// {
|
||||
// code: MainPageCode.Info,
|
||||
// name: 'О нас',
|
||||
// resName: 'info',
|
||||
// onSideBar: true,
|
||||
// icon: 'info'
|
||||
// },
|
||||
];
|
||||
|
||||
export const orderStatuses: OrderStatus = {
|
||||
Cancelled: 'Отменен',
|
||||
InProcessing: 'В обработке',
|
||||
Unconfirmed: 'Принят',
|
||||
WaitCooking: 'Принят',
|
||||
ReadyForCooking: 'Принят',
|
||||
CookingStarted: 'Готовится',
|
||||
CookingCompleted: 'Приготовлен',
|
||||
Waiting: 'В пути',
|
||||
OnWay: 'В пути',
|
||||
Delivered: 'Выполнен',
|
||||
Closed: 'Выполнен',
|
||||
};
|
||||
|
||||
export const paymentMethods: PaymentMethod[] = [
|
||||
{
|
||||
type: 'Card',
|
||||
label: 'Безналичный расчет',
|
||||
},
|
||||
{
|
||||
type: 'Cash',
|
||||
label: 'Наличными',
|
||||
},
|
||||
];
|
||||
|
||||
export const dateFilterOptions: IOptionDateFilter[] = [
|
||||
{
|
||||
name: 'Текущий месяц',
|
||||
value: 'currentMonth',
|
||||
},
|
||||
{
|
||||
name: 'Прошлый месяц',
|
||||
value: 'lastMonth',
|
||||
},
|
||||
{
|
||||
name: 'Между',
|
||||
value: 'between',
|
||||
},
|
||||
];
|
||||
|
||||
export const APP_DATE_FORMATS: MatDateFormats = {
|
||||
parse: {
|
||||
dateInput: { month: 'short', year: 'numeric', day: 'numeric' },
|
||||
},
|
||||
display: {
|
||||
dateInput: 'input',
|
||||
monthYearLabel: { year: 'numeric', month: 'numeric' },
|
||||
dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },
|
||||
monthYearA11yLabel: { year: 'numeric', month: 'long' },
|
||||
},
|
||||
};
|
||||
194
angular/src/app/app.module.ts
Normal file
194
angular/src/app/app.module.ts
Normal file
@ -0,0 +1,194 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
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 { AuthComponent } from './pages/account/auth/auth.component';
|
||||
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';
|
||||
import { HttpClientModule, HTTP_INTERCEPTORS } 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 { 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 { ShareIconsModule } from 'ngx-sharebuttons/icons';
|
||||
import { MessagingService } from './services/messaging.service';
|
||||
import { NotFoundComponent } from './pages/not-found/not-found.component';
|
||||
import { ProductsComponent } from './pages/products/products.component';
|
||||
import { CartComponent } from './pages/cart/cart.component';
|
||||
import { ListboxModule } from 'primeng/listbox';
|
||||
import { ProductModalComponent } from './components/product-modal/product-modal.component';
|
||||
import { CheckboxGroupComponent } from './components/checkbox-group/checkbox-group.component';
|
||||
import { TreeSelectModule } from 'primeng/treeselect';
|
||||
import { UserDataOrderComponent } from './components/user-data-order/user-data-order.component';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { SelectButtonModule } from 'primeng/selectbutton';
|
||||
import { CalendarModule } from 'primeng/calendar';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { InfoComponent } from './pages/info/info.component';
|
||||
import { MdbCarouselModule } from 'mdb-angular-ui-kit/carousel';
|
||||
import { StoreModule } from '@ngrx/store';
|
||||
import { configReducer } from './state/config/config.reducer';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
import { ConfigEffects } from './state/config/config.effects';
|
||||
import { PaginatorModule } from 'primeng/paginator';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { ChangeQuantityComponent } from './components/change-quantity/change-quantity.component';
|
||||
import { MenuComponent } from './components/menu/menu.component';
|
||||
import { SidebarModule } from 'primeng/sidebar';
|
||||
import { RippleModule } from 'primeng/ripple';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { ModifierComponent } from './components/modifier/modifier.component';
|
||||
import { OptionComponent } from './components/option/option.component';
|
||||
import { ChangeQuantityOptionDirective } from './directives/change-quantity-option.directive';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import {
|
||||
MatFormFieldModule,
|
||||
MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
||||
} from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatBottomSheetModule } from '@angular/material/bottom-sheet';
|
||||
import { TerminalListComponent } from './components/terminal-list/terminal-list.component';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { SnackBarComponent } from './components/snack-bar/snack-bar.component';
|
||||
import {MatDialogModule} from '@angular/material/dialog';
|
||||
import { PurchaseInfoComponent } from './components/purchase-info/purchase-info.component';
|
||||
import { DateFilterComponent } from './components/date-filter/date-filter.component';
|
||||
import {MatDatepickerModule} from '@angular/material/datepicker';
|
||||
import {MatNativeDateModule} from '@angular/material/core';
|
||||
import { profileReducer } from './state/profile/profile.reducer';
|
||||
import { ProfileEffects } from './state/profile/profile.effects';
|
||||
import { CustomerInfoInterceptor } from './interceptors/customer-info.interceptor';
|
||||
import { PurcahsesComponent } from './pages/account/purcahses/purcahses.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', redirectTo: 'products', pathMatch: 'full' },
|
||||
{ path: 'products', component: ProductsComponent },
|
||||
// { path: 'cart', component: CartComponent },
|
||||
{ path: 'account', component: AccountComponent },
|
||||
{ path: '**', component: NotFoundComponent },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
NavbarComponent,
|
||||
MainComponent,
|
||||
CardComponent,
|
||||
AuthComponent,
|
||||
AccountComponent,
|
||||
ExitComponent,
|
||||
BonusProgramComponent,
|
||||
OrdersComponent,
|
||||
OrderInfoComponent,
|
||||
FooterButtonsComponent,
|
||||
UserDataComponent,
|
||||
RefSystemComponent,
|
||||
NotFoundComponent,
|
||||
ProductsComponent,
|
||||
CartComponent,
|
||||
ProductModalComponent,
|
||||
CheckboxGroupComponent,
|
||||
UserDataOrderComponent,
|
||||
InfoComponent,
|
||||
ChangeQuantityComponent,
|
||||
MenuComponent,
|
||||
ModifierComponent,
|
||||
OptionComponent,
|
||||
ChangeQuantityOptionDirective,
|
||||
TerminalListComponent,
|
||||
SnackBarComponent,
|
||||
PurchaseInfoComponent,
|
||||
DateFilterComponent,
|
||||
PurcahsesComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
RouterModule.forRoot(routes, {
|
||||
scrollPositionRestoration: 'enabled',
|
||||
}),
|
||||
InputMaskModule,
|
||||
ProgressSpinnerModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
BrowserAnimationsModule,
|
||||
BrowserModule,
|
||||
ServiceWorkerModule.register('ngsw-worker.js', {
|
||||
enabled: environment.production,
|
||||
// Register the ServiceWorker as soon as the application is stable
|
||||
// or after 30 seconds (whichever comes first).
|
||||
registrationStrategy: 'registerWhenStable:30000',
|
||||
}),
|
||||
AngularFireModule.initializeApp(environment.firebase),
|
||||
AngularFireMessagingModule,
|
||||
ToastModule,
|
||||
ReactiveFormsModule,
|
||||
QRCodeModule,
|
||||
ShareButtonsModule.withConfig({
|
||||
debug: true,
|
||||
}),
|
||||
ShareIconsModule,
|
||||
ListboxModule,
|
||||
TreeSelectModule,
|
||||
DropdownModule,
|
||||
SelectButtonModule,
|
||||
CalendarModule,
|
||||
MatIconModule,
|
||||
MdbCarouselModule,
|
||||
StoreModule.forRoot({ config: configReducer, profile: profileReducer }),
|
||||
EffectsModule.forRoot([ConfigEffects, ProfileEffects]),
|
||||
PaginatorModule,
|
||||
InputTextModule,
|
||||
SidebarModule,
|
||||
RippleModule,
|
||||
MatTabsModule,
|
||||
MatSelectModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatBottomSheetModule,
|
||||
MatButtonModule,
|
||||
MatListModule,
|
||||
MatSnackBarModule,
|
||||
MatDialogModule,
|
||||
MatDatepickerModule,
|
||||
MatNativeDateModule
|
||||
],
|
||||
providers: [
|
||||
DialogService,
|
||||
MessageService,
|
||||
MessagingService,
|
||||
{
|
||||
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
||||
useValue: { appearance: 'outline' },
|
||||
},
|
||||
{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
useClass: CustomerInfoInterceptor,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
||||
@ -0,0 +1,19 @@
|
||||
<div class="change-quantity">
|
||||
<button (click)="changeValue({type: 'minus', variableQuantity: variableQuantity})"
|
||||
[disabled]="disabledButton.includes('minus')"
|
||||
[ngClass]="{
|
||||
'is-active': !disabledButton.includes('minus')
|
||||
}">
|
||||
-
|
||||
</button>
|
||||
<span>
|
||||
{{value}}
|
||||
</span>
|
||||
<button (click)="changeValue({type: 'plus', variableQuantity: variableQuantity})"
|
||||
[disabled]="disabledButton.includes('plus')"
|
||||
[ngClass]="{
|
||||
'is-active': !disabledButton.includes('plus')
|
||||
}">
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
@ -0,0 +1,23 @@
|
||||
:host {
|
||||
.change-quantity {
|
||||
border: 1px solid #dfdee2;
|
||||
border-radius: 1.125rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
min-width: 100px;
|
||||
padding: 0 8px;
|
||||
|
||||
button {
|
||||
background: #ffffff;
|
||||
border: 0;
|
||||
font-size: 28px;
|
||||
font-weight: 300;
|
||||
&:disabled {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ChangeQuantityComponent } from './change-quantity.component';
|
||||
|
||||
describe('ChangeQuantityComponent', () => {
|
||||
let component: ChangeQuantityComponent;
|
||||
let fixture: ComponentFixture<ChangeQuantityComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ChangeQuantityComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ChangeQuantityComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,28 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
|
||||
export interface ChangeValue {
|
||||
type: 'plus' | 'minus',
|
||||
variableQuantity: number
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-change-quantity',
|
||||
templateUrl: './change-quantity.component.html',
|
||||
styleUrls: ['./change-quantity.component.scss']
|
||||
})
|
||||
export class ChangeQuantityComponent implements OnInit {
|
||||
@Output() onChangeValue = new EventEmitter<ChangeValue>();
|
||||
@Input() variableQuantity: number = 1
|
||||
@Input() value: number = 1
|
||||
@Input() disabledButton: 'minus' | 'plus' | 'none' | string[] = 'none'
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
changeValue(changeValue: ChangeValue) {
|
||||
this.onChangeValue.emit(changeValue)
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<!-- <div *ngFor="let option of options; let index = index;" class="extra_options_checkbox">
|
||||
<div class="extra_options_label"><label>{{option.name}}</label></div>
|
||||
<div class="extra_options_value">
|
||||
<div class="woofood-cbx-wrapper">
|
||||
<input class="inp-woofood-cbx"
|
||||
[disabled]="!optionIsSelected(option) && selectedOptions.length >= modifier.restrictions.maxQuantity && (modifier.restrictions.maxQuantity > 1 || !modifier.restrictions.minQuantity)"
|
||||
[checked]="optionIsSelected(option)"
|
||||
[type]="modifier.restrictions.maxQuantity > 1 || !modifier.restrictions.minQuantity ? 'checkbox' : 'radio'"
|
||||
style="display: none" name="add_extra_option" value="{{option.id}}" id="{{option.id}}"
|
||||
(change)="onToggle(option)">
|
||||
<label class="woofood-cbx" for="{{option.id}}">
|
||||
<span>
|
||||
<svg width="12px" height="10px" viewBox="0 0 12 10">
|
||||
<polyline points="1.5 6 4.5 9 10.5 1"></polyline>
|
||||
</svg>
|
||||
</span>
|
||||
<span>{{currencySymbol}}{{option.price}}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<span *ngIf="modifier.restrictions.minQuantity > allQuantity" class="validator-text">
|
||||
Минимальное количество продуктов: {{modifier.restrictions.minQuantity}}
|
||||
</span>
|
||||
<div *ngFor="let option of options; let index = index;" class="extra_options_checkbox">
|
||||
<div class="extra_options_label"><label>{{option.name}}</label></div>
|
||||
<div class="extra_options_value">
|
||||
<app-change-quantity (onChangeValue)="changeQuantity($event, option)"
|
||||
[value]="(option.quantity || option.quantity === 0) ? option.quantity : option.restrictions.byDefault"
|
||||
[disabledButton]="getDisabledButton(option)">
|
||||
</app-change-quantity>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,94 @@
|
||||
:host {
|
||||
.extra_options_checkbox {
|
||||
width: 100%;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.extra_options_label {
|
||||
width: 70%;
|
||||
float: left;
|
||||
font-size: 14px;
|
||||
|
||||
label {
|
||||
margin: 5px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.extra_options_value {
|
||||
width: 30%;
|
||||
float: left;
|
||||
|
||||
.woofood-cbx-wrapper {
|
||||
width: 100%;
|
||||
float: left;
|
||||
|
||||
.woofood-cbx {
|
||||
margin: auto;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
line-height: 30px;
|
||||
margin-bottom: 0px;
|
||||
|
||||
span:first-child {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 8px;
|
||||
border-radius: 3px;
|
||||
transform: scale(1);
|
||||
vertical-align: middle;
|
||||
border: 1px solid #9098A9;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #cc0000;
|
||||
display: block;
|
||||
transform: scale(0);
|
||||
opacity: 1;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
svg {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 2px;
|
||||
fill: none;
|
||||
stroke: #FFFFFF;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-dasharray: 16px;
|
||||
stroke-dashoffset: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.inp-woofood-cbx:checked+.woofood-cbx span:first-child {
|
||||
background: #cc0000;
|
||||
border-color: #cc0000;
|
||||
animation: wave 0.4s ease;
|
||||
svg {
|
||||
stroke-dashoffset: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.inp-woofood-cbx:disabled+.woofood-cbx span:first-child {
|
||||
border-color: #dfdfdf !important;
|
||||
}
|
||||
}
|
||||
|
||||
.validator-text {
|
||||
color: #cc0000;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CheckboxGroupComponent } from './checkbox-group.component';
|
||||
|
||||
describe('CheckboxGroupComponent', () => {
|
||||
let component: CheckboxGroupComponent;
|
||||
let fixture: ComponentFixture<CheckboxGroupComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ CheckboxGroupComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(CheckboxGroupComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,65 @@
|
||||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||
import {CartModifier, Modifier, ModifiersGroup, Option} from 'src/app/interface/data';
|
||||
import { cloneDeep } from 'lodash/fp';
|
||||
import { ChangeValue } from '../change-quantity/change-quantity.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-checkbox-group',
|
||||
templateUrl: './checkbox-group.component.html',
|
||||
styleUrls: ['./checkbox-group.component.scss']
|
||||
})
|
||||
export class CheckboxGroupComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
@Input() modifier!: CartModifier;
|
||||
@Input() options!: Modifier[];
|
||||
@Input() currencySymbol!: string;
|
||||
@Input() selectedOptions: Modifier[] = [];
|
||||
|
||||
@Output() toggle = new EventEmitter<Modifier>();
|
||||
@Output() onChangeQuantity = new EventEmitter<any>();
|
||||
|
||||
|
||||
public allQuantity!: number
|
||||
|
||||
ngOnInit() {
|
||||
this.allQuantity = this.getAllQuantity(this.options, 'quantity')
|
||||
}
|
||||
|
||||
getAllQuantity(array: Array<any>, key: string) {
|
||||
return array.reduce((a, b) => a + (b[key] || 0), 0);
|
||||
}
|
||||
|
||||
optionIsSelected(option: Modifier): boolean{
|
||||
return !!this.selectedOptions.find(selected => selected.id === option.id);
|
||||
}
|
||||
|
||||
onToggle(option: Modifier){
|
||||
this.toggle.emit(option);
|
||||
}
|
||||
|
||||
changeQuantity(value: ChangeValue, option: Modifier) {
|
||||
this.onChangeQuantity.emit({
|
||||
value,
|
||||
option,
|
||||
modifierGroup: this.modifier
|
||||
})
|
||||
this.allQuantity = this.getAllQuantity(this.options, 'quantity')
|
||||
|
||||
}
|
||||
|
||||
getDisabledButton(option: Modifier) {
|
||||
if (!option.quantity && option.quantity !== 0) option.quantity = option.restrictions.byDefault
|
||||
const minusCondition = option.quantity === 0 || option.quantity === option.restrictions.minQuantity
|
||||
const plusCondition = option.quantity === option.restrictions.maxQuantity && option.restrictions.maxQuantity !== 0
|
||||
const maxQuantityCondition = this.allQuantity === this.modifier.restrictions.maxQuantity && this.modifier.restrictions.maxQuantity !== 0
|
||||
const minQuantityCondition = this.allQuantity === this.modifier.restrictions.minQuantity
|
||||
let result: any = 'none'
|
||||
if (minusCondition || (minQuantityCondition && !minusCondition)) result = 'minus'
|
||||
if (plusCondition || (maxQuantityCondition && !minusCondition)) result = 'plus'
|
||||
if ((maxQuantityCondition && minusCondition) || (minQuantityCondition && plusCondition)) result = ['plus', 'minus']
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<div class="date-filter-container">
|
||||
<form [formGroup]="dateFilterForm" (ngSubmit)="submitFilter()">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-select formControlName="filterType">
|
||||
<mat-option *ngFor="let option of options" [value]="option.value">{{
|
||||
option.name
|
||||
}}</mat-option>
|
||||
</mat-select>
|
||||
<mat-label>Фильтр</mat-label>
|
||||
<mat-hint>Тип</mat-hint>
|
||||
</mat-form-field>
|
||||
<div
|
||||
*ngIf="currentFilterType === 'between'"
|
||||
class="date-filter-container__date-inputs"
|
||||
>
|
||||
<mat-form-field class="date-range-input-field" appearance="fill">
|
||||
<mat-label>Интервал времени</mat-label>
|
||||
<mat-date-range-input
|
||||
[rangePicker]="campaignTwoPicker"
|
||||
[comparisonStart]="dateFilterForm.value.from"
|
||||
[comparisonEnd]="dateFilterForm.value.to"
|
||||
>
|
||||
<input matStartDate placeholder="От: " formControlName="from" />
|
||||
<input matEndDate placeholder="До: " formControlName="to" />
|
||||
</mat-date-range-input>
|
||||
<mat-hint>дд.мм.гггг – дд.мм.гггг</mat-hint>
|
||||
<mat-datepicker-toggle
|
||||
matIconSuffix
|
||||
[for]="campaignTwoPicker"
|
||||
></mat-datepicker-toggle>
|
||||
<mat-date-range-picker #campaignTwoPicker>
|
||||
<mat-date-range-picker-actions>
|
||||
<button mat-button matDateRangePickerCancel>Назад</button>
|
||||
<button mat-raised-button color="primary" matDateRangePickerApply>
|
||||
Подтвердить
|
||||
</button>
|
||||
</mat-date-range-picker-actions>
|
||||
</mat-date-range-picker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<button mat-stroked-button class="apply" type="submit">Применить</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -0,0 +1,28 @@
|
||||
:host {
|
||||
.date-filter-container {
|
||||
& > form {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1065px) {
|
||||
:host {
|
||||
.date-filter-container {
|
||||
& > form {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.date-range-input-field {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.apply {
|
||||
height: 51.67px;
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DateFilterComponent } from './date-filter.component';
|
||||
|
||||
describe('DateFilterComponent', () => {
|
||||
let component: DateFilterComponent;
|
||||
let fixture: ComponentFixture<DateFilterComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ DateFilterComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DateFilterComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,70 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import moment from 'moment';
|
||||
import { APP_DATE_FORMATS } from 'src/app/app.constants';
|
||||
import { IDateFilter, IOptionDateFilter } from 'src/app/interface/data';
|
||||
import { DateAdapterService } from 'src/app/services/date-adapter.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-date-filter[options]',
|
||||
templateUrl: './date-filter.component.html',
|
||||
styleUrls: ['./date-filter.component.scss'],
|
||||
providers: [
|
||||
{ provide: DateAdapter, useClass: DateAdapterService },
|
||||
{ provide: MAT_DATE_FORMATS, useValue: APP_DATE_FORMATS },
|
||||
{ provide: MAT_DATE_LOCALE, useValue: 'ru-RU' }
|
||||
],
|
||||
})
|
||||
export class DateFilterComponent implements OnInit {
|
||||
@Input() defaultFilterType!: string;
|
||||
@Output() onSubmitFilter = new EventEmitter<any>();
|
||||
@Input() options!: IOptionDateFilter[];
|
||||
|
||||
public currentFilterType!: string;
|
||||
|
||||
public dateFilterForm = new FormGroup({
|
||||
filterType: new FormControl<string>(''),
|
||||
from: new FormControl<Date | null>(null),
|
||||
to: new FormControl<Date | null>(null),
|
||||
});
|
||||
|
||||
constructor(private _snackBar: MatSnackBar) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.currentFilterType = this.defaultFilterType
|
||||
? this.defaultFilterType
|
||||
: this.options[0].value;
|
||||
const filterType = this.dateFilterForm.get('filterType');
|
||||
filterType?.setValue(this.currentFilterType);
|
||||
filterType?.valueChanges.subscribe({
|
||||
next: (value) => {
|
||||
this.currentFilterType = value ?? '';
|
||||
const fromControl = this.dateFilterForm.get('from')
|
||||
const toControl = this.dateFilterForm.get('to')
|
||||
|
||||
if (this.currentFilterType === 'between') {
|
||||
this.dateFilterForm.controls.from.addValidators([
|
||||
Validators.required,
|
||||
]);
|
||||
this.dateFilterForm.controls.to.addValidators([Validators.required]);
|
||||
} else {
|
||||
fromControl?.clearValidators()
|
||||
toControl?.clearValidators()
|
||||
}
|
||||
fromControl?.updateValueAndValidity()
|
||||
toControl?.updateValueAndValidity()
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
submitFilter() {
|
||||
if (!this.dateFilterForm.valid) {
|
||||
this._snackBar.open('Заполните все поля!', 'Ок', {
|
||||
duration: 2000,
|
||||
});
|
||||
}
|
||||
this.onSubmitFilter.emit(this.dateFilterForm.value)
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@ button {
|
||||
margin-right: 1rem;
|
||||
padding: 4px 21px;
|
||||
margin-top: 8px;
|
||||
background-color: #09467f;
|
||||
background-color: var(--orange-main);
|
||||
color: #fff;
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
@ -15,7 +15,7 @@
|
||||
align-items: center;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: #09467f;
|
||||
background: var(--orange-main);
|
||||
border: solid #fff 1px !important;
|
||||
color: #fff;
|
||||
border-radius: 100%;
|
||||
54
angular/src/app/components/menu/menu.component.html
Normal file
54
angular/src/app/components/menu/menu.component.html
Normal file
@ -0,0 +1,54 @@
|
||||
<nav class="main-menu-container" isShow="false" #menu>
|
||||
<ul>
|
||||
<ng-container
|
||||
*ngFor="
|
||||
let page of mainPageList;
|
||||
let index = index;
|
||||
let last = last;
|
||||
let first = first
|
||||
"
|
||||
>
|
||||
<li
|
||||
*ngIf="page.onSideBar && !last"
|
||||
class="main-menu-container__item"
|
||||
[ngClass]="{
|
||||
cart: page.resName === 'cart'
|
||||
}"
|
||||
[attr.data-counter]="page.resName === 'cart' ? cartCount : null"
|
||||
[routerLink]="page.resName"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
<mat-icon
|
||||
[ngClass]="{
|
||||
'mat-icon': true
|
||||
}"
|
||||
>
|
||||
{{ page.icon }}
|
||||
</mat-icon>
|
||||
<a>
|
||||
{{ page.name }}
|
||||
</a>
|
||||
</li>
|
||||
</ng-container>
|
||||
<li
|
||||
*ngIf="mainPageList[mainPageList.length - 1].onSideBar"
|
||||
class="main-menu-container__item"
|
||||
[ngClass]="{
|
||||
cart: true
|
||||
}"
|
||||
[attr.data-counter]="cartCount"
|
||||
(click)="changeMainPage(mainPageList[mainPageList.length - 1])"
|
||||
>
|
||||
<mat-icon
|
||||
[ngClass]="{
|
||||
'mat-icon': true
|
||||
}"
|
||||
>
|
||||
{{ mainPageList[mainPageList.length - 1].icon }}
|
||||
</mat-icon>
|
||||
<a>
|
||||
{{ mainPageList[mainPageList.length - 1].name }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
122
angular/src/app/components/menu/menu.component.scss
Normal file
122
angular/src/app/components/menu/menu.component.scss
Normal file
@ -0,0 +1,122 @@
|
||||
@import "src/sass/mixins";
|
||||
|
||||
:host {
|
||||
.main-menu-container {
|
||||
// position: fixed;
|
||||
// width: 100%;
|
||||
// bottom: 0;
|
||||
// left: 0;
|
||||
// margin: 0;
|
||||
// z-index: 777;
|
||||
// height: 57px;
|
||||
// border-top: solid 1px #dfdfdf;
|
||||
// background-color: #fff;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
font-size: 14px;
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: row;
|
||||
gap: 32px;
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
gap: 0px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2px;
|
||||
color: #b5b5b9;
|
||||
transition: color 0.3s ease;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
color: #252525;
|
||||
|
||||
a {
|
||||
border-bottom: 2px solid #e16f38;
|
||||
}
|
||||
}
|
||||
|
||||
@include hover-supported() {
|
||||
color: #252525;
|
||||
|
||||
a {
|
||||
border-bottom: 2px solid #e16f38;
|
||||
}
|
||||
}
|
||||
a {
|
||||
border-bottom: 2px solid #e16f3800;
|
||||
}
|
||||
|
||||
& .mat-icon {
|
||||
display: none;
|
||||
|
||||
&.is-active {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
&.cart {
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: attr(data-counter);
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
right: -18px;
|
||||
top: -8px;
|
||||
background: #d7120b;
|
||||
border-radius: 50px;
|
||||
min-width: 1.2rem;
|
||||
line-height: 1.2rem;
|
||||
font-size: 0.8rem;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.main-menu-container {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
z-index: 777;
|
||||
border-top: solid 1px #dfdfdf;
|
||||
background-color: #fff;
|
||||
padding-bottom: 16px;
|
||||
padding-top: 8px;
|
||||
|
||||
ul {
|
||||
li {
|
||||
&.cart {
|
||||
&::before {
|
||||
right: 34px;
|
||||
top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
& .mat-icon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
angular/src/app/components/menu/menu.component.spec.ts
Normal file
23
angular/src/app/components/menu/menu.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MenuComponent } from './menu.component';
|
||||
|
||||
describe('MenuComponent', () => {
|
||||
let component: MenuComponent;
|
||||
let fixture: ComponentFixture<MenuComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MenuComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(MenuComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
48
angular/src/app/components/menu/menu.component.ts
Normal file
48
angular/src/app/components/menu/menu.component.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { PageListMain } from 'src/app/app.constants';
|
||||
import { Page } from 'src/app/interface/data';
|
||||
import { CartService } from 'src/app/services/cart.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-menu',
|
||||
templateUrl: './menu.component.html',
|
||||
styleUrls: ['./menu.component.scss'],
|
||||
})
|
||||
export class MenuComponent implements OnInit {
|
||||
@Output() toggleMenu = new EventEmitter();
|
||||
readonly mainPageList = PageListMain;
|
||||
public cartCount = 0;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private cartService: CartService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.cartCount = this.cartService.cartCount;
|
||||
this.cartService.cartCount$.subscribe({
|
||||
next: (count) => {
|
||||
this.cartCount = count;
|
||||
document
|
||||
.querySelectorAll('.cart')[0]
|
||||
.setAttribute('data-counter', this.cartCount.toString());
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
changeMainPage(page: Page, event?: MouseEvent): void {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (page.resName === 'cart') {
|
||||
this.toggleMenu.emit()
|
||||
return
|
||||
}
|
||||
this.router.navigate([page.resName], {
|
||||
// relativeTo: this.route,
|
||||
// queryParamsHandling: 'merge',
|
||||
});
|
||||
}
|
||||
}
|
||||
12
angular/src/app/components/modifier/modifier.component.html
Normal file
12
angular/src/app/components/modifier/modifier.component.html
Normal file
@ -0,0 +1,12 @@
|
||||
<div class="modifier-container">
|
||||
<ng-content select="[counter]"></ng-content>
|
||||
<h3>{{ modifier.name }}</h3>
|
||||
<div class="modifier-container__options">
|
||||
<app-option
|
||||
*ngFor="let option of modifier.options"
|
||||
[option]="option"
|
||||
[modifier]="modifier"
|
||||
[product]="product"
|
||||
></app-option>
|
||||
</div>
|
||||
</div>
|
||||
15
angular/src/app/components/modifier/modifier.component.scss
Normal file
15
angular/src/app/components/modifier/modifier.component.scss
Normal file
@ -0,0 +1,15 @@
|
||||
:host {
|
||||
.modifier-container {
|
||||
margin-bottom: 32px;
|
||||
&__options {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ModifierComponent } from './modifier.component';
|
||||
|
||||
describe('ModifierComponent', () => {
|
||||
let component: ModifierComponent;
|
||||
let fixture: ComponentFixture<ModifierComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ModifierComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ModifierComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
19
angular/src/app/components/modifier/modifier.component.ts
Normal file
19
angular/src/app/components/modifier/modifier.component.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { CartModifier, Modifier } from 'src/app/interface/data';
|
||||
import { CartProduct } from 'src/app/models/cart-product';
|
||||
|
||||
@Component({
|
||||
selector: 'app-modifier[modifier][product]',
|
||||
templateUrl: './modifier.component.html',
|
||||
styleUrls: ['./modifier.component.scss']
|
||||
})
|
||||
export class ModifierComponent implements OnInit {
|
||||
@Input() product!: CartProduct
|
||||
@Input() modifier!: CartModifier
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
6
angular/src/app/components/navbar/navbar.component.html
Normal file
6
angular/src/app/components/navbar/navbar.component.html
Normal file
@ -0,0 +1,6 @@
|
||||
<div class="container-navbar">
|
||||
<img src="./assets/sakura-logo.png" alt="Логотип">
|
||||
<!-- <span class="menu" (click)="showMenu()">Меню</span> -->
|
||||
<app-cart #cart></app-cart>
|
||||
<app-menu (toggleMenu)="cart.toggleSideBar()"></app-menu>
|
||||
</div>
|
||||
44
angular/src/app/components/navbar/navbar.component.scss
Normal file
44
angular/src/app/components/navbar/navbar.component.scss
Normal file
@ -0,0 +1,44 @@
|
||||
.container-navbar {
|
||||
box-sizing: border-box;
|
||||
padding: 16px 54px;
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
img {
|
||||
height: 68px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.menu {
|
||||
color: #252525;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
letter-spacing: 2px;
|
||||
border-bottom: 2px solid #e16f38;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.container-navbar {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 549px) {
|
||||
.container-navbar {
|
||||
.menu {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,4 +12,13 @@ export class NavbarComponent implements OnInit {
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
showMenu() {
|
||||
const menu = document.getElementsByClassName('main-menu-container')
|
||||
if (menu[0].getAttribute('isShow') === 'true') {
|
||||
menu[0].setAttribute('isShow', 'false')
|
||||
} else {
|
||||
menu[0].setAttribute('isShow', 'true')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
17
angular/src/app/components/option/option.component.html
Normal file
17
angular/src/app/components/option/option.component.html
Normal file
@ -0,0 +1,17 @@
|
||||
<div
|
||||
appChangeQuantityOption
|
||||
[option]="option"
|
||||
[modifier]="modifier"
|
||||
[product]="product"
|
||||
[ngClass]="{
|
||||
'option-container': true,
|
||||
'selected': option.quantity
|
||||
}"
|
||||
[attr.quantity]="option.quantity"
|
||||
>
|
||||
<img
|
||||
src="{{ option.image?.length ? option.image : './assets/no-image.png' }}"
|
||||
alt="{{ option.name }}"
|
||||
/>
|
||||
<h4>{{ option.name }}</h4>
|
||||
</div>
|
||||
67
angular/src/app/components/option/option.component.scss
Normal file
67
angular/src/app/components/option/option.component.scss
Normal file
@ -0,0 +1,67 @@
|
||||
:host {
|
||||
width: 100%;
|
||||
max-width: 170px;
|
||||
.option-container {
|
||||
align-items: center;
|
||||
border: 1px solid #d8d8d8;
|
||||
border-radius: 1.125rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 0;
|
||||
padding-bottom: 12px;
|
||||
position: relative;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
&.selected {
|
||||
border-color: var(--orange-main);
|
||||
}
|
||||
|
||||
&[quantity] {
|
||||
&::before {
|
||||
content: attr(quantity);
|
||||
display: block;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
background-color: var(--orange-main);
|
||||
position: absolute;
|
||||
border-radius: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
right: 10px;
|
||||
top: 6px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
&[quantity="0"] {
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
:host {
|
||||
max-width: calc(50% - 6px);
|
||||
}
|
||||
}
|
||||
23
angular/src/app/components/option/option.component.spec.ts
Normal file
23
angular/src/app/components/option/option.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { OptionComponent } from './option.component';
|
||||
|
||||
describe('OptionComponent', () => {
|
||||
let component: OptionComponent;
|
||||
let fixture: ComponentFixture<OptionComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ OptionComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(OptionComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
20
angular/src/app/components/option/option.component.ts
Normal file
20
angular/src/app/components/option/option.component.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { CartModifier, Modifier } from 'src/app/interface/data';
|
||||
import { CartProduct } from 'src/app/models/cart-product';
|
||||
|
||||
@Component({
|
||||
selector: 'app-option[option][modifier][product]',
|
||||
templateUrl: './option.component.html',
|
||||
styleUrls: ['./option.component.scss']
|
||||
})
|
||||
export class OptionComponent implements OnInit {
|
||||
@Input() option!: Modifier
|
||||
@Input() modifier!: CartModifier;
|
||||
@Input() product!: CartProduct;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
<div class="product-modal">
|
||||
<div class="product-modal__information-container" [ngStyle]="{
|
||||
'max-width': product.modifiers_group.length ? 'calc(50% - 16px)' : '100%'
|
||||
}">
|
||||
<img
|
||||
src="{{
|
||||
product.image?.length ? product.image : './assets/no-image.png'
|
||||
}}"
|
||||
alt="{{ product.name }}"
|
||||
/>
|
||||
<p class="product-modal__description">{{product.description}}</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
*ngIf="product.modifiers_group.length"
|
||||
class="product-modal__modifiers-container"
|
||||
>
|
||||
<!-- <ng-container *ngIf="product.modifiers_group.length">
|
||||
<div *ngFor="let modifierGroup of cartProduct.modifiers" [attr.isShow]="false"
|
||||
[ngClass]="{'product-modal__modifier': true, 'no-valid': isValidate && modifierGroup.allQuantity < modifierGroup.restrictions.minQuantity}" #modifierContainer>
|
||||
<a (click)="setOptionsView($event, modifierContainer)">
|
||||
{{modifierGroup.name}}
|
||||
<span [ngClass]="{
|
||||
'product-modal__modifier-icon': true,
|
||||
'isShow': getIsShow(modifierContainer)
|
||||
}"></span>
|
||||
</a>
|
||||
<div [ngClass]="{
|
||||
'options-container': true,
|
||||
'isShow': getIsShow(modifierContainer)
|
||||
}">
|
||||
<app-checkbox-group [modifier]="modifierGroup" [options]="modifierGroup.options"
|
||||
currencySymbol="₽" (onChangeQuantity)="changeQuantity($event)">
|
||||
</app-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container> -->
|
||||
<app-modifier
|
||||
*ngFor="let modifier of cartProduct.modifiers; let index = index"
|
||||
[modifier]="modifier"
|
||||
[product]="cartProduct"
|
||||
>
|
||||
<span counter>{{ index + 1 }} из {{ cartProduct.modifiers.length }}</span>
|
||||
</app-modifier>
|
||||
</div>
|
||||
|
||||
<div class="product-modal__footer">
|
||||
<!-- <app-change-quantity (onChangeValue)="changeProductAmount()"></app-change-quantity> -->
|
||||
<span class="product-modal__footer-price">{{ cartProduct.finalPrice }}₽</span>
|
||||
<div class="product-modal__footer-buttons">
|
||||
<app-change-quantity [disabledButton]="cartProduct.amount === 1 ? 'minus' : 'none'" [value]="cartProduct.amount" (onChangeValue)="changeProductAmount($event)"></app-change-quantity>
|
||||
<button class="product-modal__add-to-cart" (click)="addToCart($event)">
|
||||
Добавить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p-toast
|
||||
position="bottom-center"
|
||||
key="cC"
|
||||
(onClose)="onReject()"
|
||||
[baseZIndex]="5000"
|
||||
>
|
||||
<ng-template let-message pTemplate="message">
|
||||
<div class="flex flex-column" style="flex: 1">
|
||||
<div class="text-center">
|
||||
<i class="pi pi-exclamation-triangle" style="font-size: 3rem"></i>
|
||||
<h4>{{ message.summary }}</h4>
|
||||
<p style="font-weight: 600">{{ message.detail }}</p>
|
||||
</div>
|
||||
<div class="grid p-fluid">
|
||||
<div class="col-6">
|
||||
<button
|
||||
type="button"
|
||||
pButton
|
||||
(click)="onConfirm()"
|
||||
label="Да"
|
||||
class="p-button-success"
|
||||
></button>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<button
|
||||
type="button"
|
||||
pButton
|
||||
(click)="onReject()"
|
||||
label="Нет"
|
||||
class="p-button-secondary"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-toast>
|
||||
@ -0,0 +1,139 @@
|
||||
:host {
|
||||
.product-modal {
|
||||
position: relative;
|
||||
padding-bottom: 39px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 32px;
|
||||
|
||||
&__information-container {
|
||||
img {
|
||||
width: 100%;
|
||||
border-radius: 1.125rem;
|
||||
object-fit: contain;
|
||||
max-height: 50vh;
|
||||
}
|
||||
}
|
||||
|
||||
&__description {
|
||||
margin-top: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
// & > img {
|
||||
// width: 50%;
|
||||
// border-radius: 1.125rem;
|
||||
// height: fit-content;
|
||||
// object-fit: cover;
|
||||
// }
|
||||
|
||||
&__footer {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
bottom: -14px;
|
||||
padding-top: 16px;
|
||||
border-top: solid #d1d1d1 1px;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__footer-price {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&__footer-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
&__add-to-cart {
|
||||
padding: 8px 26px;
|
||||
background: var(--orange-main);
|
||||
border: none;
|
||||
border-radius: 1.125rem;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&__modifier {
|
||||
border: 1px solid rgba(128, 128, 128, 0.23);
|
||||
margin: 0.5em 0;
|
||||
padding-bottom: 6px;
|
||||
border-radius: 1.125rem;
|
||||
|
||||
a {
|
||||
width: 100%;
|
||||
display: block;
|
||||
padding: 0.75em;
|
||||
border-radius: 0.15em;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.options-container {
|
||||
transition: opacity 0.5s ease;
|
||||
padding-left: 16px;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
|
||||
&.isShow {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.no-valid {
|
||||
border-color: #cc0000;
|
||||
}
|
||||
}
|
||||
|
||||
&__modifiers-container {
|
||||
height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&__modifier-icon {
|
||||
float: right;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: "\\";
|
||||
transform: rotate(346deg);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "/";
|
||||
transform: rotate(14deg);
|
||||
}
|
||||
|
||||
&.isShow {
|
||||
&::before {
|
||||
transform: rotate(76deg);
|
||||
}
|
||||
|
||||
&::after {
|
||||
transform: rotate(104deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1070px) {
|
||||
.product-modal {
|
||||
flex-direction: column;
|
||||
|
||||
&__information-container {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ProductModalComponent } from './product-modal.component';
|
||||
|
||||
describe('ProductModalComponent', () => {
|
||||
let component: ProductModalComponent;
|
||||
let fixture: ComponentFixture<ProductModalComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ProductModalComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ProductModalComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,137 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog';
|
||||
import { AllData, CartModifier, Modifier, ModifiersGroup, Option, Product } from 'src/app/interface/data';
|
||||
import { CartProduct } from 'src/app/models/cart-product';
|
||||
import { CartService } from 'src/app/services/cart.service';
|
||||
import { WpJsonService } from 'src/app/services/wp-json.service';
|
||||
import { ChangeValue } from '../change-quantity/change-quantity.component';
|
||||
import { CookiesService } from 'src/app/services/cookies.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-product-modal',
|
||||
templateUrl: './product-modal.component.html',
|
||||
styleUrls: ['./product-modal.component.scss']
|
||||
})
|
||||
export class ProductModalComponent implements OnInit {
|
||||
public product!: Product;
|
||||
public allData!: AllData;
|
||||
public modifiersGroups!: ModifiersGroup[];
|
||||
public modifiers!: Modifier[];
|
||||
public cartProduct!: CartProduct;
|
||||
public isValidate: boolean = false;
|
||||
private selectedTerminal: any;
|
||||
|
||||
constructor(
|
||||
public dialogRef: DynamicDialogRef,
|
||||
public config: DynamicDialogConfig,
|
||||
private wpJsonService: WpJsonService,
|
||||
private cartService: CartService,
|
||||
private messageService: MessageService,
|
||||
private cookiesService: CookiesService
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.product = this.config.data.product
|
||||
this.modifiersGroups = this.config.data.modifiersGroups
|
||||
this.modifiers = this.config.data.modifiers
|
||||
this.selectedTerminal = this.config.data.selectedTerminal
|
||||
this.cartProduct = new CartProduct(this.product.id, this.product.name, this.modifiersFilter(), this.modifiers, this.product.price);
|
||||
}
|
||||
|
||||
modifiersFilter(): ModifiersGroup[] {
|
||||
return this.modifiersGroups.filter((value) => this.product.modifiers_group.includes(value.id))
|
||||
}
|
||||
|
||||
optionsFilter(modifierGroup: ModifiersGroup): Modifier[] {
|
||||
return this.modifiers.filter((modifier) => modifier.groupId === modifierGroup.id)
|
||||
}
|
||||
|
||||
selectedOptions(modifier: ModifiersGroup): Modifier[] {
|
||||
const cartModifier = this.cartProduct.modifiers.find(cartModifier => cartModifier.id === modifier.id)
|
||||
if (modifier.restrictions.maxQuantity === 1 && modifier.restrictions.minQuantity === 1) {
|
||||
cartModifier?.options.push(this.optionsFilter(modifier)[0])
|
||||
}
|
||||
return cartModifier?.options ?? [];
|
||||
}
|
||||
|
||||
addOption(modifier: ModifiersGroup, option: Modifier) {
|
||||
const modif = this.cartProduct.modifiers.find((modif) => modif.id === modifier.id);
|
||||
const optionSelectedCartIndex = modif?.options.findIndex((modif) => modif.id === option.id)
|
||||
if (modifier.restrictions.maxQuantity === 1 && modifier.restrictions.minQuantity === 1) {
|
||||
if (modif?.options) {
|
||||
modif.options.length = 0
|
||||
}
|
||||
} else {
|
||||
if ((optionSelectedCartIndex || optionSelectedCartIndex === 0) && optionSelectedCartIndex !== -1) {
|
||||
modif?.options.splice(optionSelectedCartIndex, 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
modif?.options.push(option)
|
||||
}
|
||||
|
||||
getIsShow(element: HTMLDivElement) {
|
||||
const isShow = Object.values(element.attributes).find((value) => value.name === 'isshow')?.value
|
||||
return isShow === 'true'
|
||||
}
|
||||
|
||||
setOptionsView(event: Event, element: HTMLDivElement) {
|
||||
if (event) {
|
||||
event.preventDefault()
|
||||
}
|
||||
const isShow = this.getIsShow(element)
|
||||
if (isShow) {
|
||||
element.setAttribute('isShow', 'false')
|
||||
return
|
||||
}
|
||||
element.setAttribute('isShow', 'true')
|
||||
}
|
||||
|
||||
changeProductAmount({type, variableQuantity}: ChangeValue) {
|
||||
if (type === 'plus') {
|
||||
this.cartProduct.increment()
|
||||
} else {
|
||||
this.cartProduct.decrement()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onReject() {
|
||||
this.messageService.clear('cC');
|
||||
}
|
||||
|
||||
onConfirm() {
|
||||
this.cartService.clearCart()
|
||||
this.cartService.changeTerminal(this.config.data.selectedTerminal)
|
||||
this.cartService.addToCart(this.cartProduct);
|
||||
this.messageService.clear('cC');
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
addToCart(event: Event) {
|
||||
if (event) {
|
||||
event.preventDefault()
|
||||
}
|
||||
const cookiesTerminal = JSON.parse(this.cookiesService.getItem('selectedTerminal') || '')
|
||||
|
||||
for (let modifiersGroup of this.cartProduct.modifiers) {
|
||||
const isValidModifier = modifiersGroup.allQuantity < modifiersGroup.restrictions.minQuantity
|
||||
if (isValidModifier) {
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: 'Заполните все модификаторы!'
|
||||
});
|
||||
this.isValidate = true
|
||||
return
|
||||
}
|
||||
}
|
||||
if (this.selectedTerminal.id !== cookiesTerminal?.id) {
|
||||
this.messageService.add({ key: 'cC', sticky: true, severity: 'warn', summary: 'В заказе могут быть товары только из одного магазина, очистить корзину для создания нового заказа?' });
|
||||
return
|
||||
}
|
||||
this.cartService.changeTerminal(this.config.data.selectedTerminal)
|
||||
this.cartService.addToCart(this.cartProduct);
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<h2 mat-dialog-title>{{ moment(purchaseInfo.date).format("DD.MM.YYYY") }}</h2>
|
||||
<mat-dialog-content>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr
|
||||
*ngFor="let good of purchaseInfo.goods_list"
|
||||
class="woocommerce-orders-table__row woocommerce-orders-table__row--status-processing order"
|
||||
>
|
||||
<td
|
||||
class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-name"
|
||||
data-title="Название"
|
||||
>
|
||||
{{ good.name }}
|
||||
</td>
|
||||
<td
|
||||
class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-price"
|
||||
data-title="Цена"
|
||||
>
|
||||
{{ good.price }}
|
||||
</td>
|
||||
<td
|
||||
class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-quantity"
|
||||
data-title="Количество"
|
||||
>
|
||||
{{ good.quantity }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<span class="amount">Сумма: {{ purchaseInfo.sum }}</span>
|
||||
<button mat-stroked-button mat-dialog-close>Закрыть</button>
|
||||
</mat-dialog-actions>
|
||||
@ -0,0 +1,45 @@
|
||||
:host {
|
||||
.woocommerce-orders-table {
|
||||
&__cell {
|
||||
padding: 8px 4px;
|
||||
border-bottom: 2px solid #dee2e6;
|
||||
text-align: right !important;
|
||||
display: block;
|
||||
&::before {
|
||||
content: attr(data-title) ": ";
|
||||
font-weight: 700;
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
&__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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.mat-dialog-actions {
|
||||
justify-content: space-between;
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PurchaseInfoComponent } from './purchase-info.component';
|
||||
|
||||
describe('PurchaseInfoComponent', () => {
|
||||
let component: PurchaseInfoComponent;
|
||||
let fixture: ComponentFixture<PurchaseInfoComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PurchaseInfoComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(PurchaseInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,24 @@
|
||||
import { Component, OnInit, Inject } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import moment from 'moment';
|
||||
import { PurchaseInfo } from 'src/app/interface/data';
|
||||
|
||||
@Component({
|
||||
selector: 'app-purchase-info',
|
||||
templateUrl: './purchase-info.component.html',
|
||||
styleUrls: ['./purchase-info.component.scss']
|
||||
})
|
||||
export class PurchaseInfoComponent implements OnInit {
|
||||
public purchaseInfo!: PurchaseInfo;
|
||||
readonly moment = moment;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<PurchaseInfoComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: {purchaseInfo: PurchaseInfo},
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.purchaseInfo = this.data.purchaseInfo
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
<div class="snack-bar">
|
||||
<span>{{ data.text }}</span>
|
||||
<div class="buttons">
|
||||
<button mat-stroked-button matSnackBarAction (click)="buttonClick('no')">
|
||||
Нет
|
||||
</button>
|
||||
<button
|
||||
mat-stroked-button
|
||||
matSnackBarAction
|
||||
color="error"
|
||||
(click)="buttonClick('yes')"
|
||||
>
|
||||
Да
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,18 @@
|
||||
:host {
|
||||
.snack-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
span {
|
||||
color: #323232;
|
||||
}
|
||||
.buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SnackBarComponent } from './snack-bar.component';
|
||||
|
||||
describe('SnackBarComponent', () => {
|
||||
let component: SnackBarComponent;
|
||||
let fixture: ComponentFixture<SnackBarComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SnackBarComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SnackBarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
23
angular/src/app/components/snack-bar/snack-bar.component.ts
Normal file
23
angular/src/app/components/snack-bar/snack-bar.component.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { MatSnackBarRef, MAT_SNACK_BAR_DATA } from '@angular/material/snack-bar';
|
||||
|
||||
@Component({
|
||||
selector: 'app-snack-bar',
|
||||
templateUrl: './snack-bar.component.html',
|
||||
styleUrls: ['./snack-bar.component.scss']
|
||||
})
|
||||
export class SnackBarComponent implements OnInit {
|
||||
|
||||
constructor(@Inject(MAT_SNACK_BAR_DATA) public data: {text: string}, private _matSnackBarRef: MatSnackBarRef<SnackBarComponent>) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
buttonClick(result: 'yes' | 'no') {
|
||||
if (result === 'yes') {
|
||||
this._matSnackBarRef.dismissWithAction()
|
||||
} else {
|
||||
this._matSnackBarRef.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
<mat-nav-list>
|
||||
<div
|
||||
mat-list-item
|
||||
*ngFor="let item of data.list"
|
||||
[ngClass]="{
|
||||
'list-item': true,
|
||||
active: item.id === data.active.id
|
||||
}"
|
||||
(click)="selectItem(item)"
|
||||
>
|
||||
<span matListItemTitle>{{ item.label }}</span>
|
||||
</div>
|
||||
</mat-nav-list>
|
||||
@ -0,0 +1,21 @@
|
||||
@import 'src/sass/mixins';
|
||||
|
||||
:host {
|
||||
.list-item {
|
||||
padding: 12px;
|
||||
font-size: 14px;
|
||||
border-radius: 6px;
|
||||
transition: all .4s ease;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
&.active {
|
||||
background-color: var(--orange-main);
|
||||
color: #fff;
|
||||
}
|
||||
@include hover-supported() {
|
||||
background-color: #eee;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TerminalListComponent } from './terminal-list.component';
|
||||
|
||||
describe('TerminalListComponent', () => {
|
||||
let component: TerminalListComponent;
|
||||
let fixture: ComponentFixture<TerminalListComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ TerminalListComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TerminalListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,25 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { MatBottomSheetRef, MAT_BOTTOM_SHEET_DATA } from '@angular/material/bottom-sheet';
|
||||
|
||||
export interface IListData {
|
||||
list: Array<any>;
|
||||
active: any;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-terminal-list',
|
||||
templateUrl: './terminal-list.component.html',
|
||||
styleUrls: ['./terminal-list.component.scss']
|
||||
})
|
||||
export class TerminalListComponent implements OnInit {
|
||||
|
||||
constructor(@Inject(MAT_BOTTOM_SHEET_DATA) public data: IListData, private _bottomSheetRef: MatBottomSheetRef<TerminalListComponent>) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
selectItem(item: any) {
|
||||
this._bottomSheetRef.dismiss(item)
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,159 @@
|
||||
<div
|
||||
*ngIf="mainFormGroup && !loading; else loadingEl"
|
||||
class="woocommerce-shipping-fields__field-wrapper"
|
||||
>
|
||||
<form
|
||||
*ngIf="!showAuthoriztion; else authEl"
|
||||
(ngSubmit)="submit()"
|
||||
[formGroup]="mainFormGroup"
|
||||
action="false"
|
||||
autocomplete="on"
|
||||
>
|
||||
<h2 class="order_form__title">Оформление заказа</h2>
|
||||
<p *ngIf="hasError" class="request-error-message">
|
||||
Произошла ошибка. Попробуйте позже.
|
||||
</p>
|
||||
<div class="order_form" formGroupName="userDataForm">
|
||||
<p сlass="form-row form-row-wide">
|
||||
<input
|
||||
formControlName="first_name"
|
||||
id="first_name"
|
||||
pInputText
|
||||
placeholder="Ваше имя"
|
||||
type="text"
|
||||
/>
|
||||
</p>
|
||||
<div *ngIf="deliverData.deliveryType?.name === 'Доставка'">
|
||||
<p сlass="form-row form-row-last">
|
||||
<input
|
||||
formControlName="flat"
|
||||
id="flat"
|
||||
pInputText
|
||||
placeholder="Квартира"
|
||||
type="number"
|
||||
min="1"
|
||||
/>
|
||||
</p>
|
||||
<p сlass="form-row form-row-wide">
|
||||
<input
|
||||
formControlName="street"
|
||||
id="street"
|
||||
pInputText
|
||||
placeholder="Улица"
|
||||
type="text"
|
||||
/>
|
||||
</p>
|
||||
<p сlass="form-row form-row-first">
|
||||
<input
|
||||
formControlName="house"
|
||||
id="house"
|
||||
pInputText
|
||||
placeholder="Номер дома"
|
||||
type="text"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<p сlass="form-row form-row-wide">
|
||||
<label class="terminal-list-label">Пункты самовывоза</label>
|
||||
<p-dropdown
|
||||
*ngIf="deliverData.deliveryType?.name === 'Самовывоз'"
|
||||
[options]="terminalList"
|
||||
formControlName="selectedTerminal"
|
||||
placeholder="Пункты выдачи"
|
||||
optionLabel="label"
|
||||
></p-dropdown>
|
||||
</p>
|
||||
<!-- <div *ngIf="deliverData.deliveryType?.name === 'Самовывоз'" class="terminal-list-container">
|
||||
<div *ngFor="let terminal of terminalList" [ngClass]="{
|
||||
'terminal-container': true,
|
||||
'selected': terminal.label === selectedTerminal.label
|
||||
}">
|
||||
<img src="{{terminal.image}}" alt="{{terminal.address}}">
|
||||
<span class="terminal-container__name">{{terminal.label}}</span>
|
||||
<span class="terminal-container__address">{{terminal.address}}</span>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<div formGroupName="deliveryDataForm">
|
||||
<p сlass="form-row form-row-wide">
|
||||
<label class="terminal-list-label">Способ получения</label>
|
||||
<p-dropdown
|
||||
[options]="deliveryTypes"
|
||||
formControlName="deliveryType"
|
||||
placeholder="Доставка"
|
||||
optionLabel="name"
|
||||
(onChange)="changeDeliveryType($event)"
|
||||
></p-dropdown>
|
||||
</p>
|
||||
<p сlass="form-row form-row-wide">
|
||||
<label id="deliveryDate">Время выдачи</label>
|
||||
<p-calendar
|
||||
formControlName="deliveryDate"
|
||||
[showTime]="true"
|
||||
[showSeconds]="false"
|
||||
[touchUI]="true"
|
||||
inputId="time"
|
||||
placeholder="Время выдачи"
|
||||
dateFormat="dd.mm.yy"
|
||||
></p-calendar>
|
||||
</p>
|
||||
<p сlass="form-row form-row-wide">
|
||||
<p-dropdown
|
||||
[options]="paymentMethods"
|
||||
formControlName="paymentMethod"
|
||||
optionLabel="label"
|
||||
placeholder="Тип оплаты"
|
||||
>
|
||||
</p-dropdown>
|
||||
<!-- *Оплата бонусами -->
|
||||
</p>
|
||||
<!-- <p сlass="form-row form-row-last">
|
||||
<input [maxLength]="255" id="promo-code" pInputText placeholder="Промокод" type="text">
|
||||
</p> -->
|
||||
<p сlass="form-row form-row-wide">
|
||||
<textarea
|
||||
[maxLength]="255"
|
||||
cols="30"
|
||||
formControlName="comment"
|
||||
pInputTextarea
|
||||
placeholder="Комментарий"
|
||||
rows="1"
|
||||
></textarea>
|
||||
</p>
|
||||
</div>
|
||||
<div class="bootom-info">
|
||||
<div class="subtotal">
|
||||
<span class="products-count">Товаров: {{ order.products.length }}</span>
|
||||
<span class="woocommerce-Price-amount amount"
|
||||
><bdi
|
||||
><span class="woocommerce-Price-currencySymbol">{{
|
||||
order.products[0].currency_symbol
|
||||
}}</span
|
||||
>{{ order.price }}</bdi
|
||||
></span
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
class="elementor-button elementor-button--checkout elementor-size-md"
|
||||
>
|
||||
<span class="elementor-button-text">Оформить заказ</span>
|
||||
</button>
|
||||
</div>
|
||||
<p *ngIf="showMyMessage" style="color: red; font-size: 20px">
|
||||
Такой адрес не найден! Введите правильный адрес
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<ng-template #loadingEl>
|
||||
<div
|
||||
class="angular-spinner-container"
|
||||
style="width: fit-content; height: 100%; margin: 16px auto"
|
||||
>
|
||||
<p-progressSpinner styleClass="angular-spinner"></p-progressSpinner>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #authEl>
|
||||
<app-auth (phoneConfirmed)="phoneConfirmed()"></app-auth>
|
||||
</ng-template>
|
||||
@ -0,0 +1,159 @@
|
||||
:host {
|
||||
.woocommerce-shipping-fields__field-wrapper {
|
||||
margin: 8px auto 100px auto;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.order_form__title {
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
color: #000000;
|
||||
border: 1px solid #000000;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica,
|
||||
Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", Segoe UI Symbol;
|
||||
font-size: 1rem;
|
||||
color: #495057;
|
||||
background: #ffffff;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 1px solid #ced4da;
|
||||
transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
border-radius: 4px;
|
||||
&.ng-dirty.ng-invalid {
|
||||
border-color: red;
|
||||
}
|
||||
&.ng-invalid.ng-touched {
|
||||
border-color: red;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 52px;
|
||||
color: #000000;
|
||||
border: 1px solid #000000;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica,
|
||||
Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", Segoe UI Symbol;
|
||||
font-size: 1rem;
|
||||
color: #495057;
|
||||
background: #ffffff;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 1px solid #ced4da;
|
||||
transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.terminal-list-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
margin: 8px 0 16px;
|
||||
|
||||
.terminal-container {
|
||||
width: calc(50% - 8px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border: solid var(--orange-main) 1px;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
padding-bottom: 16px;
|
||||
opacity: 0.5;
|
||||
text-align: center;
|
||||
|
||||
&.selected {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
&__name {
|
||||
}
|
||||
|
||||
&__address {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.terminal-list-label {
|
||||
margin-left: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-row-wide {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#deliveryDate {
|
||||
margin-left: 8px;
|
||||
margin-bottom: 4px;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.bootom-info {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
bottom: 0;
|
||||
padding: 18px;
|
||||
background: #fff;
|
||||
z-index: 3;
|
||||
border-top: solid 1px #e7e7e7;
|
||||
|
||||
.subtotal {
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.products-count {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.elementor-button--checkout {
|
||||
background-color: var(--orange-main);
|
||||
color: #fff;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: none;
|
||||
padding: 12px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 650px) {
|
||||
.bootom-info {
|
||||
width: 450px;
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserDataOrderComponent } from './user-data-order.component';
|
||||
|
||||
describe('UserDataOrderComponent', () => {
|
||||
let component: UserDataOrderComponent;
|
||||
let fixture: ComponentFixture<UserDataOrderComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ UserDataOrderComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(UserDataOrderComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,269 @@
|
||||
import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { DeliveryData, DeliveryType, PaymentMethod, UserData } from 'src/app/interface/data';
|
||||
import { paymentMethods } from "../../app.constants";
|
||||
import { OrderService } from "../../services/order.service";
|
||||
import { AutocompleteService } from "../../services/autocomplete.service";
|
||||
import { StreetValidator } from "../../validators/street.validator";
|
||||
import { CartService } from 'src/app/services/cart.service';
|
||||
import { environment } from "../../../environments/environment";
|
||||
import { MessageService } from "primeng/api";
|
||||
import { WpJsonService } from "../../services/wp-json.service";
|
||||
import { HttpClient, HttpClientModule } from '@angular/common/http';
|
||||
import { CookiesService } from 'src/app/services/cookies.service';
|
||||
import moment from 'moment';
|
||||
import { Order } from 'src/app/models/order';
|
||||
import { Store } from '@ngrx/store';
|
||||
import * as fromConfig from '../../state/config/config.reducer'
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { GetTerminalsService } from 'src/app/services/get-terminals.service';
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-data-order',
|
||||
templateUrl: './user-data-order.component.html',
|
||||
styleUrls: ['./user-data-order.component.scss']
|
||||
})
|
||||
export class UserDataOrderComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Output() orderSubmitted = new EventEmitter<number>();
|
||||
@Output() userNotFound = new EventEmitter<null>();
|
||||
readonly cities = environment.cities;
|
||||
public paymentMethods!: PaymentMethod[];
|
||||
public loading = false;
|
||||
public hasError = false;
|
||||
public mainFormGroup!: FormGroup;
|
||||
public deliveryTypes: DeliveryType[] = [];
|
||||
public new_street!: string | null;
|
||||
public street!: string;
|
||||
public new_house!: string | null;
|
||||
public checkAddress: boolean = true;
|
||||
public showMyMessage: boolean = false;
|
||||
public order!: Order;
|
||||
public showAuthoriztion = false;
|
||||
private intervalTimeDelivery!: any
|
||||
|
||||
public userData: UserData = {
|
||||
name: '',
|
||||
first_name: null,
|
||||
last_name: null,
|
||||
street: null,
|
||||
house: null,
|
||||
flat: null,
|
||||
city: this.cities[0],
|
||||
phone: null,
|
||||
selectedTerminal: null
|
||||
};
|
||||
public deliverData: DeliveryData = {
|
||||
deliveryDate: null,
|
||||
deliveryType: null,
|
||||
paymentMethod: null,
|
||||
comment: '',
|
||||
persons: 1,
|
||||
orderid: 0
|
||||
};
|
||||
public terminalList!: any;
|
||||
public selectedTerminal!: any;
|
||||
|
||||
checkoutConfig$ = this.store.select(fromConfig.selectCheckout);
|
||||
checkoutConfig!: any;
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private orderService: OrderService,
|
||||
private autoCompleteService: AutocompleteService,
|
||||
private streetValidator: StreetValidator,
|
||||
private cartService: CartService,
|
||||
private messageService: MessageService,
|
||||
private wpJsonService: WpJsonService,
|
||||
private http: HttpClient,
|
||||
private cookiesService: CookiesService,
|
||||
private store: Store,
|
||||
private _getTerminals: GetTerminalsService
|
||||
) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.checkAuthorization(true)
|
||||
this._createMainForm();
|
||||
this.getTerminalList();
|
||||
this.checkoutConfig$.subscribe({
|
||||
next: (value: any) => {
|
||||
this.checkoutConfig = value
|
||||
}
|
||||
})
|
||||
this.deliverData.deliveryDate = moment().add(this.checkoutConfig?.timeDelivery?.changeTime?.defaultValue || 0, 'minutes').toDate()
|
||||
this.intervalTimeDelivery = setInterval(() => {
|
||||
this.mainFormGroup.controls['deliveryDataForm'].patchValue({
|
||||
deliveryDate: moment().add(this.checkoutConfig?.timeDelivery?.changeTime?.defaultValue || 0, 'minutes').toDate()
|
||||
})
|
||||
}, 60_000)
|
||||
this.paymentMethods = this.checkoutConfig.payments.values
|
||||
this.deliverData.paymentMethod = this.paymentMethods[this.checkoutConfig.payments.default]
|
||||
|
||||
}
|
||||
|
||||
async getTerminalList() {
|
||||
// this.http.get('./assets/terminal_list1.json').subscribe({
|
||||
// next: (value) => {
|
||||
// this.terminalList = value
|
||||
// },
|
||||
// error: (err) => {
|
||||
// console.error(err);
|
||||
// }
|
||||
// })
|
||||
const _getTerminals = await this._getTerminals.getTerminalList()
|
||||
this.terminalList = _getTerminals.list
|
||||
this.selectedTerminal = _getTerminals.active
|
||||
}
|
||||
|
||||
checkAuthorization(showAuthoriztion: boolean, forced = false) {
|
||||
if (!this.getToken() || forced) {
|
||||
this.showAuthoriztion = showAuthoriztion
|
||||
}
|
||||
}
|
||||
|
||||
getToken(): string | void {
|
||||
return this.cookiesService.getItem('token');
|
||||
}
|
||||
|
||||
phoneConfirmed() {
|
||||
this._createMainForm();
|
||||
this.showAuthoriztion = false
|
||||
this.checkAuthorization(true)
|
||||
}
|
||||
|
||||
changeDeliveryType(event: any) {
|
||||
this.deliverData.deliveryType = event.value;
|
||||
if (this.deliverData.deliveryType?.name) {
|
||||
this.changeValidators(this.deliverData.deliveryType.name)
|
||||
}
|
||||
}
|
||||
|
||||
changeValidators(name: string) {
|
||||
const comment = this.mainFormGroup.controls['deliveryDataForm'].value.comment;
|
||||
const streetValidators = name === 'Доставка' ? [Validators.required, Validators.minLength(2), Validators.maxLength(255),] : []
|
||||
const houseValidators = name === 'Доставка' ? [Validators.required, Validators.maxLength(10),] : []
|
||||
const userDataForm = this.fb.group({
|
||||
phone: [this.userData.phone],
|
||||
first_name: [this.userData.first_name, [Validators.required, Validators.minLength(2), Validators.maxLength(255)]],
|
||||
// last_name: [this.userData.last_name, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
|
||||
street: [this.userData.street, streetValidators],
|
||||
house: [this.userData.house, houseValidators],
|
||||
flat: [this.userData.flat, []],
|
||||
// city: [this.userData.city, [Validators.required]],
|
||||
});
|
||||
const deliveryDataForm = this.fb.group({
|
||||
deliveryDate: [this.deliverData.deliveryDate, []],
|
||||
deliveryType: [this.deliverData.deliveryType, [Validators.required]],
|
||||
paymentMethod: [this.deliverData.paymentMethod, [Validators.required]],
|
||||
// persons: [this.deliverData.persons, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
|
||||
comment: [comment, [Validators.maxLength(255),]],
|
||||
});
|
||||
|
||||
this.mainFormGroup = this.fb.group({
|
||||
userDataForm,
|
||||
deliveryDataForm,
|
||||
});
|
||||
}
|
||||
|
||||
submit(): void {
|
||||
const mainControls = this.mainFormGroup.controls;
|
||||
if (this.mainFormGroup.invalid) {
|
||||
Object.keys(mainControls).forEach(groupName => {
|
||||
const childGroupControls = (mainControls[groupName] as FormGroup).controls;
|
||||
Object.keys(childGroupControls).forEach(controlName => {
|
||||
childGroupControls[controlName].markAsTouched();
|
||||
});
|
||||
});
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: 'Заполните обязательные поля',
|
||||
})
|
||||
return;
|
||||
}
|
||||
this.submitOrder();
|
||||
}
|
||||
|
||||
async submitOrder() {
|
||||
this.loading = true;
|
||||
const userData: UserData = this.mainFormGroup.controls['userDataForm'].getRawValue();
|
||||
userData.phone = this.userData.phone;
|
||||
const deliveryData = this.mainFormGroup.controls['deliveryDataForm'].getRawValue()
|
||||
deliveryData.orderid = Math.floor(Math.random() * (10 ** 12))
|
||||
this.orderService.setUserData(userData);
|
||||
this.orderService.setDeliveryData(deliveryData);
|
||||
|
||||
this.orderService.submit().subscribe({
|
||||
next: (_) => {
|
||||
this.loading = false;
|
||||
this.cartService.clearCart();
|
||||
this.orderSubmitted.next(deliveryData.orderid);
|
||||
},
|
||||
error: () => {
|
||||
this.loading = false;
|
||||
this.hasError = true;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private async _createMainForm(): Promise<void> {
|
||||
try {
|
||||
this.loading = true;
|
||||
const userDataForm = await this._createUserDataForm();
|
||||
const deliveryDataForm = await this._createDeliveryDataForm();
|
||||
this.mainFormGroup = this.fb.group({
|
||||
userDataForm,
|
||||
deliveryDataForm,
|
||||
});
|
||||
this.loading = false;
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Error: ', e);
|
||||
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: 'Произошла ошибка',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private async _createUserDataForm(): Promise<FormGroup> {
|
||||
this.order = await this.orderService.getOrder(true);
|
||||
if (this.order.userData?.errorCode === "Customer_CustomerNotFound") {
|
||||
this.userNotFound.emit(null)
|
||||
}
|
||||
this.userData = Object.assign({}, this.userData, this.order.userData);
|
||||
this.userData.city = this.cities[0];
|
||||
this.userData.phone = this.order.phone;
|
||||
// await this.autoCompleteService.setCity(this.userData.city);
|
||||
const isSelfDelivery = this.deliverData.deliveryType?.name === "Самовывоз"
|
||||
return this.fb.group({
|
||||
selectedTerminal: [{ value: this.selectedTerminal, disabled: true }, []],
|
||||
phone: [this.userData.phone],
|
||||
first_name: [{value: this.userData.name, disabled: true}, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
|
||||
// last_name: [this.userData.last_name, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
|
||||
street: [{ value: this.userData.street, disabled: isSelfDelivery }, isSelfDelivery ?? [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
|
||||
house: [{ value: this.userData.house, disabled: isSelfDelivery }, isSelfDelivery ?? [Validators.required, Validators.maxLength(10), Validators.pattern('^\\d+[-|\\d]+\\d+$|^\\d*$')]],
|
||||
flat: [this.userData.flat, []],
|
||||
// city: [this.userData.city, [Validators.required]],
|
||||
});
|
||||
}
|
||||
|
||||
private async _createDeliveryDataForm(): Promise<FormGroup> {
|
||||
this.deliveryTypes = this.checkoutConfig.delivery.filter((value: any) => value.isPickUp);
|
||||
this.deliverData.deliveryType = this.deliveryTypes[0];
|
||||
return this.fb.group({
|
||||
deliveryDate: [{ value: this.deliverData.deliveryDate, disabled: this.checkoutConfig.timeDelivery.changeTime.disabled }, []],
|
||||
deliveryType: [{ value: this.deliverData.deliveryType, disabled: this.checkoutConfig.delivery.disabled || this.deliveryTypes.length < 2 }, [Validators.required]],
|
||||
paymentMethod: [{ value: this.deliverData.paymentMethod, disabled: this.checkoutConfig.payments.disabled }, [Validators.required]],
|
||||
persons: [this.deliverData.persons, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
|
||||
comment: [this.deliverData.comment, [Validators.maxLength(255),]]
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
clearInterval(this.intervalTimeDelivery)
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
import { ChangeQuantityOptionDirective } from './change-quantity-option.directive';
|
||||
|
||||
describe('ChangeQuantityOptionDirective', () => {
|
||||
it('should create an instance', () => {
|
||||
const directive = new ChangeQuantityOptionDirective();
|
||||
expect(directive).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,65 @@
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
Input,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { CartModifier, Modifier } from '../interface/data';
|
||||
import { CartProduct } from '../models/cart-product';
|
||||
|
||||
@Directive({
|
||||
selector: '[appChangeQuantityOption]',
|
||||
})
|
||||
export class ChangeQuantityOptionDirective implements OnInit {
|
||||
@Input() option!: Modifier;
|
||||
@Input() modifier!: CartModifier;
|
||||
@Input() product!: CartProduct;
|
||||
|
||||
constructor(private el: ElementRef) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.option.quantity = this.option.restrictions.byDefault;
|
||||
|
||||
if (!this.modifier.lastChangeOption && this.option.restrictions.byDefault) {
|
||||
this.modifier.lastChangeOption = this.option.idLocal;
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('click') onOptionClick() {
|
||||
this.changeQuantity({ option: this.option, modifier: this.modifier });
|
||||
}
|
||||
|
||||
changeQuantity({
|
||||
option,
|
||||
modifier,
|
||||
}: {
|
||||
option: Modifier;
|
||||
modifier: CartModifier;
|
||||
}): void {
|
||||
if (!option.quantity && option.quantity !== 0) {
|
||||
return;
|
||||
}
|
||||
const quantity = option.quantity;
|
||||
const allQuantity = modifier.allQuantity;
|
||||
const maxOptionQ = option.restrictions.maxQuantity;
|
||||
const minOptionQ = option.restrictions.minQuantity;
|
||||
const maxModifierQ = modifier.restrictions.maxQuantity;
|
||||
const minModifierQ = modifier.restrictions.minQuantity;
|
||||
const plusCondition =
|
||||
(quantity < maxOptionQ && allQuantity < maxModifierQ) ||
|
||||
(maxOptionQ === 0 && (allQuantity < maxModifierQ || maxModifierQ === 0));
|
||||
|
||||
if (!plusCondition && quantity > minOptionQ && allQuantity > minModifierQ) {
|
||||
this.product.decrementOption(modifier.idLocal, option.idLocal);
|
||||
}
|
||||
if (allQuantity === maxModifierQ && modifier.lastChangeOption) {
|
||||
this.product.decrementOption(modifier.idLocal, modifier.lastChangeOption);
|
||||
this.changeQuantity({ option, modifier });
|
||||
}
|
||||
if (plusCondition) {
|
||||
this.product.incrementOption(modifier.idLocal, option.idLocal);
|
||||
modifier.lastChangeOption = option.idLocal;
|
||||
}
|
||||
}
|
||||
}
|
||||
50
angular/src/app/interceptors/customer-info.interceptor.ts
Normal file
50
angular/src/app/interceptors/customer-info.interceptor.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
HttpRequest,
|
||||
HttpHandler,
|
||||
HttpEvent,
|
||||
HttpInterceptor,
|
||||
HttpResponse,
|
||||
HttpErrorResponse,
|
||||
} from '@angular/common/http';
|
||||
import { Observable, tap } from 'rxjs';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
|
||||
@Injectable()
|
||||
export class CustomerInfoInterceptor implements HttpInterceptor {
|
||||
constructor(private _snackBar: MatSnackBar) {}
|
||||
|
||||
intercept(
|
||||
request: HttpRequest<unknown>,
|
||||
next: HttpHandler
|
||||
): Observable<HttpEvent<unknown>> {
|
||||
return next.handle(request).pipe(
|
||||
tap(
|
||||
(event) => {
|
||||
if (event instanceof HttpResponse) {
|
||||
const body = event.body as any;
|
||||
const url = new URL(event.url || '');
|
||||
if (url.pathname.includes('/icard-proxy/customer_info')) {
|
||||
if (
|
||||
body.customer_info?.errorCode === 'Customer_CustomerNotFound'
|
||||
) {
|
||||
this._snackBar.open(
|
||||
'Пользователь не найден в системе! Обратитесь к руководству',
|
||||
'Ок'
|
||||
);
|
||||
} else if ('Error' in body) {
|
||||
this._snackBar.open('Произошла ошибка! Попробуйте снова', 'Ок');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event instanceof HttpErrorResponse) {
|
||||
this._snackBar.open('Произошла ошибка! Попробуйте снова', 'Ок');
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
console.error('ERROR FROM INTERCEPTOR: ', err);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
353
angular/src/app/interface/data.ts
Normal file
353
angular/src/app/interface/data.ts
Normal file
@ -0,0 +1,353 @@
|
||||
import { CartProduct } from '../models/cart-product';
|
||||
|
||||
export enum PageCode {
|
||||
// Auth,
|
||||
BonusProgram,
|
||||
Orders,
|
||||
RefSystem,
|
||||
UserData,
|
||||
Transactions,
|
||||
Logout
|
||||
}
|
||||
|
||||
export enum MainPageCode {
|
||||
Account,
|
||||
Products,
|
||||
Cart,
|
||||
Info,
|
||||
}
|
||||
|
||||
export interface Page {
|
||||
code: PageCode | MainPageCode;
|
||||
component?: any;
|
||||
name: string;
|
||||
description?: string;
|
||||
getMethod?: string;
|
||||
resName?: string;
|
||||
onSideBar: boolean;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface UserDataForm {
|
||||
first_name: string;
|
||||
birthdate: string;
|
||||
gender: string;
|
||||
}
|
||||
|
||||
export interface BonusProgramAccount {
|
||||
// BonusProgramName: string;
|
||||
// BonusProgramTypeID: string;
|
||||
CardNumber: string;
|
||||
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;
|
||||
orderNumber: number;
|
||||
transactionCreateDate: string;
|
||||
orderSum: number;
|
||||
transactionSum: number;
|
||||
transactionType: 'CancelPayFromWallet' | 'PayFromWallet' | 'RefillWallet';
|
||||
more_info: PurchaseInfo | null;
|
||||
}
|
||||
|
||||
export interface PurchaseInfo {
|
||||
date: string;
|
||||
goods_list: [
|
||||
{
|
||||
name: string;
|
||||
price: number;
|
||||
quantity: number;
|
||||
}
|
||||
];
|
||||
number: string;
|
||||
sum: 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 DeliveryType {
|
||||
// cost: number;
|
||||
// title: string;
|
||||
// id: number;
|
||||
// type: string;
|
||||
name: string;
|
||||
iikoId: string;
|
||||
iikoName: string;
|
||||
isPickUp: boolean;
|
||||
}
|
||||
|
||||
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: string;
|
||||
name: string;
|
||||
price: number;
|
||||
image: string;
|
||||
image_gallery: string[];
|
||||
category_id: number;
|
||||
description: string;
|
||||
stock_status: string;
|
||||
currency_symbol: string;
|
||||
modifier_data: CartModifier[];
|
||||
short_description: string;
|
||||
guid: string;
|
||||
groupId: string;
|
||||
modifiers_group: string[];
|
||||
}
|
||||
|
||||
export interface AllData {
|
||||
datetime: string;
|
||||
groups: Group[];
|
||||
products: Product[];
|
||||
modifiers_groups: ModifiersGroup[];
|
||||
modifiers: Modifier[];
|
||||
categories: any[];
|
||||
}
|
||||
|
||||
export interface Group {
|
||||
id: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface ModifiersGroup {
|
||||
name: string;
|
||||
id: string;
|
||||
restrictions: {
|
||||
minQuantity: number;
|
||||
maxQuantity: number;
|
||||
freeQuantity: number;
|
||||
byDefault: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Modifier {
|
||||
id: string;
|
||||
idLocal: string;
|
||||
name: string;
|
||||
groupId: string;
|
||||
price?: number;
|
||||
quantity?: number;
|
||||
image?: string;
|
||||
restrictions: {
|
||||
minQuantity: number;
|
||||
maxQuantity: number;
|
||||
freeQuantity: number;
|
||||
byDefault: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CartModifier {
|
||||
lastChangeOption?: string;
|
||||
id: string;
|
||||
idLocal: string;
|
||||
name: string;
|
||||
options: Modifier[];
|
||||
allQuantity: number;
|
||||
restrictions: {
|
||||
minQuantity: number;
|
||||
maxQuantity: number;
|
||||
freeQuantity: number;
|
||||
byDefault: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Cart {
|
||||
products: CartProduct[];
|
||||
}
|
||||
|
||||
// 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;
|
||||
groupId: string;
|
||||
restrictions: {
|
||||
minQuantity: number;
|
||||
maxQuantity: number;
|
||||
freeQuantity: number;
|
||||
byDefault: number;
|
||||
};
|
||||
}
|
||||
|
||||
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 | null;
|
||||
deliveryDate: Date | null;
|
||||
deliveryType: DeliveryType | null;
|
||||
persons: number;
|
||||
comment: string;
|
||||
orderid: number;
|
||||
}
|
||||
|
||||
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;
|
||||
selectedTerminal: ITerminal | null;
|
||||
name: string;
|
||||
errorCode?: string;
|
||||
}
|
||||
|
||||
export interface ITerminal {
|
||||
label: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface IOptionDateFilter {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface IDateFilter {
|
||||
filterType: string;
|
||||
from?: Date;
|
||||
to?: Date;
|
||||
}
|
||||
|
||||
export interface ICustomerInfo {
|
||||
anonymized: boolean;
|
||||
birthday: string | Date;
|
||||
cards: ICardCustomer[];
|
||||
categories: ICategoriesCustomer[];
|
||||
comment: string | null;
|
||||
consentStatus: number;
|
||||
cultureName: string;
|
||||
email: string | null;
|
||||
id: string;
|
||||
iikoCardOrdersSum: number;
|
||||
isBlocked: boolean;
|
||||
isDeleted: boolean;
|
||||
middleName: string | null;
|
||||
name: string;
|
||||
personalDataConsentFrom: string | null;
|
||||
personalDataConsentTo: string | null;
|
||||
personalDataProcessingFrom: string | null;
|
||||
personalDataProcessingTo: string | null;
|
||||
phone: string;
|
||||
rank: string | null;
|
||||
referrerId: string | null;
|
||||
sex: number;
|
||||
shouldReceiveLoyaltyInfo: boolean;
|
||||
shouldReceiveOrderStatusInfo: boolean;
|
||||
shouldReceivePromoActionsInfo: boolean;
|
||||
surname: string;
|
||||
userData: any;
|
||||
walletBalances: IWalletBalance[];
|
||||
errorCode?: string;
|
||||
|
||||
first_name: string | null;
|
||||
last_name: string | null;
|
||||
street: string | null;
|
||||
house: string | null;
|
||||
flat: string | null;
|
||||
city: string;
|
||||
selectedTerminal: ITerminal | null;
|
||||
}
|
||||
|
||||
export interface ICardCustomer {
|
||||
Id: string;
|
||||
IsActivated: boolean;
|
||||
NetworkId: string | null;
|
||||
Number: string;
|
||||
OrganizationId: string;
|
||||
OrganizationName: string | null;
|
||||
Track: string;
|
||||
ValidToDate: string | Date;
|
||||
}
|
||||
|
||||
export interface ICategoriesCustomer {}
|
||||
|
||||
export interface IWalletBalance {
|
||||
balance: number;
|
||||
wallet: {
|
||||
id: string;
|
||||
name: string;
|
||||
programType: string;
|
||||
type: string;
|
||||
};
|
||||
}
|
||||
85
angular/src/app/models/cart-product.ts
Normal file
85
angular/src/app/models/cart-product.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import { CartModifier, Modifier, ModifiersGroup } from "../interface/data";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export class CartProduct {
|
||||
|
||||
|
||||
constructor(id: string, name: string, modifiers: ModifiersGroup[] = [], options: Modifier[], price: number, amount: number = 1) {
|
||||
this.id = id;
|
||||
this.guid = uuidv4();
|
||||
this.amount = amount;
|
||||
this.name = name;
|
||||
this.price = price * amount
|
||||
this.modifiers = modifiers.map(modifier => ({
|
||||
name: modifier.name,
|
||||
id: modifier.id,
|
||||
idLocal: uuidv4(),
|
||||
options: JSON.parse(JSON.stringify(options)).filter((option: any) => option.groupId === modifier.id),
|
||||
restrictions: modifier.restrictions,
|
||||
get allQuantity() {
|
||||
return this.options.reduce((a: any, b: any) => a + (b['quantity'] || 0), 0)
|
||||
}
|
||||
}));
|
||||
this.modifiers.forEach((modifier) => {
|
||||
modifier.options.forEach((option) => {
|
||||
option.idLocal = uuidv4()
|
||||
if (!option.quantity && option.quantity !== 0) {
|
||||
option.quantity = option.restrictions.byDefault
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
id: string;
|
||||
guid: string;
|
||||
amount: number;
|
||||
name: string;
|
||||
price: number;
|
||||
modifiers: CartModifier[];
|
||||
|
||||
increment(): void {
|
||||
this.amount++;
|
||||
}
|
||||
|
||||
decrement(): void {
|
||||
if (this.amount > 0) {
|
||||
this.amount--;
|
||||
}
|
||||
}
|
||||
|
||||
incrementOption(modifierId: string, optionId: string): void {
|
||||
const modifier = this.modifiers.find((modifier) => modifier.idLocal === modifierId)
|
||||
const option = modifier?.options.find((option) => option.idLocal === optionId)
|
||||
if (!option?.quantity && option?.quantity !== 0) return
|
||||
option.quantity++
|
||||
}
|
||||
|
||||
decrementOption(modifierId: string, optionId: string): void {
|
||||
const modifier = this.modifiers.find((modifier) => modifier.idLocal === modifierId)
|
||||
const option = modifier?.options.find((option) => option.idLocal === optionId)
|
||||
if (!option?.quantity && option?.quantity !== 0) return
|
||||
option.quantity--
|
||||
}
|
||||
|
||||
get finalPrice(): number{
|
||||
const modifiersPrice = this.modifiers.reduce<number>((previousValue, currentValue) => {
|
||||
return previousValue + currentValue.options.reduce<number>((previousOptionValue, currentOptionValue) => {
|
||||
return previousOptionValue + Number(currentOptionValue.price ? currentOptionValue.price * (currentOptionValue.quantity ?? 0) : 0);
|
||||
}, 0);
|
||||
}, 0);
|
||||
return (Number(this.price) + modifiersPrice) * this.amount;
|
||||
}
|
||||
|
||||
// addOption(modifier: ModifiersGroup, option: Modifier): void {
|
||||
// const productModifier = this.modifiers.find(value => value.id === modifier.id);
|
||||
// if (productModifier) {
|
||||
// const optionIndex = productModifier.options.findIndex(value => value.id === option.id);
|
||||
// if (optionIndex === -1) {
|
||||
// productModifier.options.push(option);
|
||||
// }
|
||||
// else {
|
||||
// productModifier.options.splice(optionIndex, 1)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
111
angular/src/app/models/order-product.ts
Normal file
111
angular/src/app/models/order-product.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import { CartModifier, Modifier, Product } from '../interface/data';
|
||||
|
||||
export interface OrderProductToJson {
|
||||
id: string;
|
||||
amount: number;
|
||||
price: number;
|
||||
quantity: number;
|
||||
name: string;
|
||||
options?: OrderProductOption[];
|
||||
}
|
||||
|
||||
export interface OrderProductOption {
|
||||
option: string;
|
||||
variants: {
|
||||
variant: string;
|
||||
id: string;
|
||||
quantity: number | undefined;
|
||||
}[];
|
||||
id: string;
|
||||
}
|
||||
|
||||
export class OrderProduct implements Product {
|
||||
constructor(product: Product, guid: string, amount: number = 1) {
|
||||
this.category_id = product.category_id;
|
||||
this.currency_symbol = product.currency_symbol;
|
||||
this.description = product.description;
|
||||
this.id = product.id;
|
||||
this.image_gallery = product.image_gallery;
|
||||
this.image = product.image;
|
||||
this.modifier_data = product.modifier_data;
|
||||
this.name = product.name;
|
||||
this.price = product.price;
|
||||
this.stock_status = product.stock_status;
|
||||
this.amount = amount;
|
||||
this.guid = guid;
|
||||
this.short_description = product.short_description;
|
||||
this.groupId = product.groupId;
|
||||
this.modifiers_group = product.modifiers_group;
|
||||
}
|
||||
|
||||
public amount: number;
|
||||
public category_id: number;
|
||||
public currency_symbol: string;
|
||||
public description: string;
|
||||
public short_description: string;
|
||||
public id: string;
|
||||
public image_gallery: string[];
|
||||
public image: string;
|
||||
public modifier_data: CartModifier[];
|
||||
public name: string;
|
||||
public price: number;
|
||||
public stock_status: string;
|
||||
public guid: string;
|
||||
public groupId: string;
|
||||
public modifiers_group: string[];
|
||||
|
||||
get finalPrice(): number {
|
||||
const modifiersPrice = this.modifier_data.reduce<number>(
|
||||
(previousValue, currentValue) => {
|
||||
return (
|
||||
previousValue +
|
||||
currentValue.options.reduce<number>(
|
||||
(previousOptionValue, currentOptionValue) => {
|
||||
return (
|
||||
previousOptionValue +
|
||||
Number(
|
||||
currentOptionValue.price
|
||||
? currentOptionValue.price *
|
||||
(currentOptionValue.quantity ?? 0)
|
||||
: 0
|
||||
)
|
||||
);
|
||||
},
|
||||
0
|
||||
)
|
||||
);
|
||||
},
|
||||
0
|
||||
);
|
||||
return (Number(this.price) + modifiersPrice) * this.amount;
|
||||
}
|
||||
|
||||
toJson(): OrderProductToJson {
|
||||
const product: OrderProductToJson = {
|
||||
id: this.id,
|
||||
amount: this.amount * this.price,
|
||||
price: this.price,
|
||||
quantity: this.amount,
|
||||
name: this.name,
|
||||
};
|
||||
const options = this.modifier_data
|
||||
?.map((modifier) => {
|
||||
return {
|
||||
option: modifier.name,
|
||||
variants:
|
||||
modifier.options
|
||||
.map((option) => ({
|
||||
variant: option.name,
|
||||
id: option.id,
|
||||
quantity: option.quantity,
|
||||
}))
|
||||
.filter((option) => option.quantity) || null,
|
||||
id: modifier.id,
|
||||
};
|
||||
})
|
||||
.filter((modifier) => modifier.variants.length);
|
||||
|
||||
if (options.length) product.options = options;
|
||||
return product;
|
||||
}
|
||||
}
|
||||
@ -2,22 +2,25 @@ import {DeliveryData, UserData} from "../interface/data";
|
||||
import {OrderProduct} from "./order-product";
|
||||
import * as moment from 'moment';
|
||||
import { CookiesService } from "../services/cookies.service";
|
||||
import { environment } from "src/environments/environment";
|
||||
|
||||
export interface OrderInfo {
|
||||
products: OrderProduct[];
|
||||
userData?: UserData;
|
||||
userData: UserData | null;
|
||||
deliveryData?: DeliveryData;
|
||||
phone: string;
|
||||
token: string | undefined;
|
||||
terminal_id: string;
|
||||
}
|
||||
|
||||
export class Order {
|
||||
|
||||
public products: OrderProduct[];
|
||||
public userData?: UserData;
|
||||
public userData!: UserData | null;
|
||||
public deliveryData?: DeliveryData;
|
||||
public phone: string;
|
||||
public token: string | undefined;
|
||||
public terminal_id: string;
|
||||
|
||||
constructor(
|
||||
orderInfo: OrderInfo
|
||||
@ -27,6 +30,7 @@ export class Order {
|
||||
this.deliveryData = orderInfo.deliveryData;
|
||||
this.phone = orderInfo.phone;
|
||||
this.token = orderInfo.token;
|
||||
this.terminal_id = orderInfo.terminal_id;
|
||||
}
|
||||
|
||||
get price(): number {
|
||||
@ -36,33 +40,30 @@ export class Order {
|
||||
toJson(): any {
|
||||
const date = moment(this.deliveryData?.deliveryDate ?? Date.now());
|
||||
return {
|
||||
items: this.products.map(product => {
|
||||
return product.toJson();
|
||||
}),
|
||||
user_data: {
|
||||
phone: this.phone,
|
||||
...this.userData
|
||||
},
|
||||
payment_method: this.deliveryData?.paymentMethod.type,
|
||||
delivery_time: date.format('HH:mm'),
|
||||
delivery_date: date.format("YYYY-MM-DD"),
|
||||
delivery_instance_id: this.deliveryData?.deliveryType?.id,
|
||||
formname: "Cart",
|
||||
paymentsystem: this.deliveryData?.paymentMethod?.type,
|
||||
phone: this.phone,
|
||||
persons: 1,
|
||||
payments: [
|
||||
{
|
||||
type: this.deliveryData?.paymentMethod.type,
|
||||
summ: this.price,
|
||||
name: this.userData?.first_name,
|
||||
payment: {
|
||||
orderid: this.deliveryData?.orderid,
|
||||
delivery_price: 0,
|
||||
products: this.products.map(product => {
|
||||
return product.toJson();
|
||||
}),
|
||||
delivery_fio: this.userData?.first_name,
|
||||
subtotal: this.price,
|
||||
delivery_comment: this.deliveryData?.comment,
|
||||
delivery: this.deliveryData?.deliveryType?.name,
|
||||
delivery_address: {
|
||||
street: this.userData?.street || '',
|
||||
house: this.userData?.house || '',
|
||||
flat: '',
|
||||
city: ''
|
||||
},
|
||||
{
|
||||
type: "crm4retail",
|
||||
summ: 0,
|
||||
payload: {
|
||||
id: "c07a10d8-ba7e-43b0-92aa-ae470060bc7d"
|
||||
}
|
||||
}
|
||||
],
|
||||
comment: this.deliveryData?.comment,
|
||||
token: this.token
|
||||
amount: this.price,
|
||||
terminal_id: this.userData?.selectedTerminal?.id || this.terminal_id
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
81
angular/src/app/pages/account/account.component.html
Normal file
81
angular/src/app/pages/account/account.component.html
Normal file
@ -0,0 +1,81 @@
|
||||
<div
|
||||
[ngClass]="{
|
||||
woocommerce: true,
|
||||
'auth-page': showAuthoriztion
|
||||
}"
|
||||
>
|
||||
<!-- <button *ngIf="!showAuthoriztion" class="logout" color="warn" mat-stroked-button (click)="logout()">Выйти</button> -->
|
||||
<div *ngIf="!showAuthoriztion; else authEl" class="account-page">
|
||||
<div *ngIf="showAuthoriztion" class="top-left-attribute"></div>
|
||||
<app-bonus-program
|
||||
[currentPage]="currentPage"
|
||||
(deauthorization)="changePage(pageList[0])"
|
||||
></app-bonus-program>
|
||||
|
||||
<mat-tab-group mat-stretch-tabs="false" mat-align-tabs="center" (selectedTabChange)="changeTabPage($event)" [(selectedIndex)]="selectedTabPage">
|
||||
<ng-container *ngFor="let page of pageList">
|
||||
<mat-tab *ngIf="page.onSideBar" label="{{ page.name }}">
|
||||
<div [ngSwitch]="page.resName" class="">
|
||||
<ng-container *ngSwitchCase="'transactions'">
|
||||
<app-orders
|
||||
[currentPage]="currentPage"
|
||||
(deauthorization)="changePage(pageList[0])"
|
||||
></app-orders>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'orders'">
|
||||
<app-purcahses
|
||||
></app-purcahses>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'user-data'">
|
||||
<app-user-data></app-user-data>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'ref-system'">
|
||||
<app-ref-system></app-ref-system>
|
||||
</ng-container>
|
||||
</div>
|
||||
</mat-tab>
|
||||
</ng-container>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
|
||||
<p-toast
|
||||
position="bottom-center"
|
||||
key="c"
|
||||
(onClose)="onReject()"
|
||||
[baseZIndex]="5000"
|
||||
>
|
||||
<ng-template let-message pTemplate="message">
|
||||
<div class="flex flex-column" style="flex: 1">
|
||||
<div class="text-center">
|
||||
<i class="pi pi-exclamation-triangle" style="font-size: 3rem"></i>
|
||||
<h4>{{ message.summary }}</h4>
|
||||
<p style="font-weight: 600">{{ message.detail }}</p>
|
||||
</div>
|
||||
<div class="grid p-fluid">
|
||||
<div class="col-6">
|
||||
<button
|
||||
type="button"
|
||||
pButton
|
||||
(click)="onConfirm()"
|
||||
label="Да"
|
||||
class="p-button-success"
|
||||
></button>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<button
|
||||
type="button"
|
||||
pButton
|
||||
(click)="onReject()"
|
||||
label="Нет"
|
||||
class="p-button-secondary"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-toast>
|
||||
</div>
|
||||
|
||||
<ng-template #authEl>
|
||||
<app-auth (phoneConfirmed)="phoneConfirmed()"></app-auth>
|
||||
</ng-template>
|
||||
218
angular/src/app/pages/account/account.component.scss
Normal file
218
angular/src/app/pages/account/account.component.scss
Normal file
@ -0,0 +1,218 @@
|
||||
:host ::ng-deep .mat-tab-body {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
:host {
|
||||
.woocommerce {
|
||||
min-height: calc(100vh - 39px);
|
||||
padding: 20px 18px 65px;
|
||||
position: relative;
|
||||
|
||||
.logout {
|
||||
float: right;
|
||||
}
|
||||
|
||||
&.auth-page {
|
||||
}
|
||||
|
||||
.main-menu-container {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
z-index: 777;
|
||||
height: 57px;
|
||||
border-top: solid 1px #dfdfdf;
|
||||
background-color: #fff;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
font-size: 14px;
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: row;
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
color: #b5b5b9;
|
||||
cursor: pointer;
|
||||
|
||||
&.mat-icon {
|
||||
color: #b5b5b9;
|
||||
|
||||
&.is-active {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
&.cart {
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: attr(data-counter);
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
right: calc(50% - 24px);
|
||||
top: 3px;
|
||||
background: #d7120b;
|
||||
border-radius: 50px;
|
||||
min-width: 1.2rem;
|
||||
line-height: 1.2rem;
|
||||
font-size: 0.8rem;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.account-page {
|
||||
max-width: 1208px;
|
||||
display: flex;
|
||||
margin: 0 auto;
|
||||
gap: 32px;
|
||||
width: 98%;
|
||||
flex-direction: row;
|
||||
nav {
|
||||
margin-bottom: 24px;
|
||||
margin-top: 26px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
ul {
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
|
||||
li {
|
||||
padding: 12px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
height: 81px;
|
||||
margin-bottom: 10px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0px 0px 5px rgb(0 0 0 / 15%);
|
||||
color: #000;
|
||||
border-radius: 15px;
|
||||
|
||||
&.is-active {
|
||||
border: solid var(--orange-main) 1px;
|
||||
// display: none;
|
||||
}
|
||||
|
||||
&.first {
|
||||
// border-radius: 7px 0 0 7px;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
|
||||
.menu-item-info {
|
||||
margin-left: 16px;
|
||||
|
||||
& > a {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
display: block;
|
||||
text-align-last: left;
|
||||
}
|
||||
|
||||
& > p {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li:nth-child(odd) {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mat-tab-group {
|
||||
width: calc(100% - 432px);
|
||||
}
|
||||
}
|
||||
|
||||
.top-left-attribute {
|
||||
width: 10px;
|
||||
height: 5px;
|
||||
background: var(--orange-main);
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 69px;
|
||||
}
|
||||
|
||||
.version {
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
bottom: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media screen and (min-width: 550px) {
|
||||
.woocommerce {
|
||||
padding-bottom: 78px;
|
||||
|
||||
.main-menu-container {
|
||||
width: 500px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
border: solid 1px #dfdfdf;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 0 6px 2px rgb(223 223 223);
|
||||
transition: top 0.4s cubic-bezier(0.57, -0.43, 0.41, 1.3);
|
||||
top: -60px;
|
||||
|
||||
&[isShow="true"] {
|
||||
top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.version {
|
||||
bottom: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 895px) {
|
||||
.woocommerce {
|
||||
.account-page {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
.mat-tab-group {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
243
angular/src/app/pages/account/account.component.ts
Normal file
243
angular/src/app/pages/account/account.component.ts
Normal file
@ -0,0 +1,243 @@
|
||||
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { CookiesService } from '../../services/cookies.service';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { MainPageCode, Page, PageCode } from '../../interface/data';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { PageList, PageListMain, PageListWithBonus } from '../../app.constants';
|
||||
import { HttpErrorResponse, HttpParams } from '@angular/common/http';
|
||||
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';
|
||||
import { CartService } from 'src/app/services/cart.service';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { getProfile, getTransactions, getTransactionsInfo } from 'src/app/state/profile/profile.actions';
|
||||
import { MatTabChangeEvent } from '@angular/material/tabs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-account',
|
||||
templateUrl: './account.component.html',
|
||||
styleUrls: ['./account.component.scss'],
|
||||
providers: [DialogService],
|
||||
})
|
||||
export class AccountComponent implements OnInit {
|
||||
@Output() setUserDataOrderPage = new EventEmitter<null>();
|
||||
@ViewChild('menu', { static: true}) menu!: ElementRef;
|
||||
public refSystemId: string = '';
|
||||
public selectedTabPage: number = 1
|
||||
|
||||
constructor(
|
||||
private cookiesService: CookiesService,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private dialogService: DialogService,
|
||||
private jsonRpcService: JsonrpcService,
|
||||
private messageService: MessageService,
|
||||
private cartService: CartService,
|
||||
private store: Store
|
||||
) { }
|
||||
|
||||
public currentPage!: Page;
|
||||
public handleHttpErrorFunc = this.handleHttpError.bind(this);
|
||||
private ref!: DynamicDialogRef;
|
||||
public version: string = environment.version;
|
||||
|
||||
readonly PageCode = PageCode;
|
||||
readonly pageList = environment.hasBonusProgram
|
||||
? PageListWithBonus
|
||||
: PageList;
|
||||
|
||||
readonly MainPageCode = MainPageCode;
|
||||
readonly mainPageList = PageListMain;
|
||||
public currentPageMain: Page = this.mainPageList[environment.production ? 1 : 1];
|
||||
public cartCount = 0;
|
||||
public showAuthoriztion = false;
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
this.checkAuthorization(true)
|
||||
|
||||
this.store.dispatch(getProfile())
|
||||
this.store.dispatch(getTransactions())
|
||||
this.store.dispatch(getTransactionsInfo())
|
||||
|
||||
|
||||
this.currentPage = this.pageList[0];
|
||||
document.body.classList.add(
|
||||
'woocommerce-account',
|
||||
'woocommerce-page',
|
||||
'woocommerce-orders'
|
||||
);
|
||||
this.route.queryParams.subscribe((params) => {
|
||||
if (!params['activeAccountPage']) {
|
||||
this.currentPage = this.pageList[0];
|
||||
return;
|
||||
}
|
||||
const currentPage = this.pageList.find((page) => {
|
||||
return page.code === Number(params['activeAccountPage']);
|
||||
});
|
||||
if (!currentPage) {
|
||||
this.currentPage = this.pageList[0];
|
||||
} else {
|
||||
this.currentPage = currentPage;
|
||||
}
|
||||
});
|
||||
this.cartCount = this.cartService.cartCount;
|
||||
this.cartService.cartCount$.subscribe({
|
||||
next: (count) => {
|
||||
this.cartCount = count;
|
||||
document.querySelectorAll('.cart')[0].setAttribute("data-counter", this.cartCount.toString())
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
phoneConfirmed(): void {
|
||||
this.refSystem();
|
||||
this.showAuthoriztion = false
|
||||
this.checkAuthorization(true)
|
||||
}
|
||||
|
||||
async refSystem() {
|
||||
const additionalInfo = (await lastValueFrom(
|
||||
this.jsonRpcService.rpc({
|
||||
method: 'getAdditionalInfo',
|
||||
params: []
|
||||
}, RpcService.authService, true)
|
||||
)).data
|
||||
this.route.queryParams.subscribe((params) => {
|
||||
if (params['refUserId']) {
|
||||
if (additionalInfo.refSystem?.code.length) {
|
||||
this.messageService.add({
|
||||
severity: 'custom',
|
||||
summary:
|
||||
'Вы уже зарегестрированы в реферальной программе!',
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.refSystemId = params['refUserId'];
|
||||
this.jsonRpcService
|
||||
.rpc(
|
||||
{
|
||||
method: 'updateAdditionalInfo',
|
||||
params: [
|
||||
{
|
||||
refSystem: {
|
||||
code: this.refSystemId,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
RpcService.authService,
|
||||
true
|
||||
)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.router.navigate([], {
|
||||
queryParams: {
|
||||
refUserId: null,
|
||||
},
|
||||
queryParamsHandling: 'merge',
|
||||
});
|
||||
this.messageService.add({
|
||||
severity: 'custom',
|
||||
summary:
|
||||
'Регистрация прошла успешна! Бонусы скоро будут начислены',
|
||||
});
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Error: ', err);
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: 'Произошла ошибка, попробуйте позже',
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
changeMainPage(page: Page, event?: MouseEvent): void {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
this.currentPageMain = page;
|
||||
this.router.navigate(['.'], {
|
||||
relativeTo: this.route,
|
||||
// queryParams: {
|
||||
// activePage: this.currentPage.code,
|
||||
// },
|
||||
queryParamsHandling: 'merge',
|
||||
});
|
||||
this.checkAuthorization(true)
|
||||
if (this.currentPageMain === this.mainPageList[2]) {
|
||||
this.checkAuthorization(false)
|
||||
}
|
||||
this.menu.nativeElement.setAttribute('isShow', 'false')
|
||||
}
|
||||
|
||||
changePage(page: Page, event?: MouseEvent): void {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
this.currentPage = page;
|
||||
this.router.navigate(['.'], {
|
||||
relativeTo: this.route,
|
||||
queryParams: {
|
||||
activeAccountPage: this.currentPage.code,
|
||||
},
|
||||
queryParamsHandling: 'merge',
|
||||
});
|
||||
}
|
||||
|
||||
checkAuthorization(showAuthoriztion: boolean, forced = false) {
|
||||
if (!this.getToken() || forced) {
|
||||
this.showAuthoriztion = showAuthoriztion
|
||||
}
|
||||
}
|
||||
|
||||
handleHttpError(error: HttpErrorResponse): void {
|
||||
if (error.status === 500) {
|
||||
this.logout();
|
||||
}
|
||||
}
|
||||
|
||||
setToken(token: string): void {
|
||||
this.cookiesService.setCookie('token', token);
|
||||
}
|
||||
|
||||
getToken(): string | void {
|
||||
return this.cookiesService.getItem('token');
|
||||
}
|
||||
|
||||
deleteToken(): void {
|
||||
this.cookiesService.deleteCookie('token');
|
||||
// this.router.navigate(['.'], {
|
||||
// queryParams: {},
|
||||
// });
|
||||
}
|
||||
|
||||
onReject() {
|
||||
this.messageService.clear('c');
|
||||
}
|
||||
|
||||
onConfirm() {
|
||||
this.deleteToken();
|
||||
this.showAuthoriztion = true;
|
||||
this.messageService.clear('c')
|
||||
}
|
||||
|
||||
changeTabPage(event: MatTabChangeEvent) {
|
||||
if (event.index === 2) {
|
||||
this.selectedTabPage = 1
|
||||
this.logout()
|
||||
}
|
||||
}
|
||||
|
||||
logout(event?: MouseEvent) {
|
||||
if (event) {
|
||||
event.preventDefault()
|
||||
}
|
||||
this.messageService.add({ key: 'c', sticky: true, severity: 'warn', summary: 'Вы уверены, что хотите выйти?' });
|
||||
}
|
||||
}
|
||||
@ -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" style="background: url('./assets/card-decorative-pattern.png') no-repeat;"></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 последних цифры позвонившего вам номера телефона. На звонок
|
||||
отвечать не нужно.
|
||||
Введите код из смс, которое пришло на Ваш номер телефона
|
||||
</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" style="background: url('./assets/card-decorative-pattern.png') no-repeat;"></div> -->
|
||||
</form>
|
||||
</ng-template>
|
||||
@ -39,7 +39,7 @@
|
||||
background: #ffffff;
|
||||
box-shadow: 0px 0px 10px rgb(0 0 0 / 15%);
|
||||
border-radius: 15px;
|
||||
min-height: 262px;
|
||||
// min-height: 262px;
|
||||
max-width: 400px;
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
@ -83,11 +83,11 @@ export class AuthComponent {
|
||||
next: (result) => {
|
||||
if (result.code === 0) {
|
||||
this.cookiesService.setCookie('token', result?.data?.token);
|
||||
this.router.navigate(['/auth'], {
|
||||
queryParams: {
|
||||
token: result?.data?.token
|
||||
},
|
||||
});
|
||||
// this.router.navigate(['/auth'], {
|
||||
// queryParams: {
|
||||
// token: result?.data?.token
|
||||
// },
|
||||
// });
|
||||
this.phoneConfirmed.emit(null);
|
||||
} else {
|
||||
this.errorConfirmCode = true;
|
||||
@ -4,27 +4,30 @@
|
||||
<div style="display: flex; justify-content: center">
|
||||
<div class="card-container">
|
||||
<div class="card-container__header">
|
||||
<img src="./assets/logo.svg" alt="Логотип карта" />
|
||||
<img src="./assets/logo.png" alt="Логотип карта" />
|
||||
<span *ngIf="accountData">#{{ accountData.CardNumber }}</span>
|
||||
</div>
|
||||
<div class="card-container__decorative-pattern">
|
||||
<!-- <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.svg" alt="" /> -->
|
||||
</div>
|
||||
<div class="card-container__content">
|
||||
<div class="info">
|
||||
<div *ngIf="accountData" class="row">
|
||||
<div *ngIf="!accountData && !loadingBonuses" class="info__row">
|
||||
Пользователь не найден, обратитесь к руководству
|
||||
</div>
|
||||
<div *ngIf="accountData" class="info__row">
|
||||
<span class="key">Имя</span>
|
||||
<span class="value" *ngIf="userName.length">{{
|
||||
<span class="value" *ngIf="userName?.length">{{
|
||||
userName
|
||||
}}</span>
|
||||
<span class="value" *ngIf="!userName.length"
|
||||
<span class="value" *ngIf="!userName?.length"
|
||||
>Не задано</span
|
||||
>
|
||||
</div>
|
||||
<div *ngIf="accountData" class="row">
|
||||
<div *ngIf="accountData" class="info__row">
|
||||
<span class="key">Баланс карты</span>
|
||||
<span class="value">{{ accountData.Bonuses }}</span>
|
||||
</div>
|
||||
@ -47,4 +50,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <button *ngIf="deviceType == 'ios'" class="add-to-wallet" (click)="addCardToWallet($event)">Добавить в Apple Wallet</button> -->
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user