mirror of
https://github.com/sergeyyarkov/digital-store.git
synced 2026-10-11 10:19:15 +03:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 020d8599b9 | |||
| 84338c9566 | |||
| 7282c740f4 | |||
| 3a77ac81ab | |||
| ec2cdc0e5b | |||
| 2728293110 | |||
| 3a17fa5235 | |||
| 0c964e2135 | |||
| af04a2619e | |||
| 73929244f4 | |||
| 3f68af1521 | |||
| d4e1eca030 | |||
| 104073cffe | |||
| 7f321d0ddb | |||
| e43ea0f34b | |||
| 2623087082 | |||
| b44bb97b8b | |||
| e4fa09f07c | |||
| c20037d8ed | |||
| c2b6e7c31c | |||
| aaa085c416 | |||
| 2b4f4610a5 | |||
| 8e79ce1e99 | |||
| c444ac2cfd | |||
| 3140c3a8ee | |||
| a63ec56170 | |||
| 308dfb34de | |||
| 316bab9ad5 | |||
| edd81cb451 | |||
| 7d7e2ebccb | |||
| ad4ff4f0f1 | |||
| 70ebf728fe | |||
| 6d6df14b27 | |||
| 332db3e55f | |||
| 3a0e325481 | |||
| a7d581dc96 | |||
|
|
21cade8a17 | ||
| 8ec78a9fae | |||
| c469d8d56f | |||
| b7b67214ab | |||
| 01b66f81f0 | |||
| 629df78a22 |
9
.babelrc
9
.babelrc
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": [
|
|
||||||
["@babel/preset-env", {
|
|
||||||
"targets": [
|
|
||||||
"last 5 versions"
|
|
||||||
]
|
|
||||||
}]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
97
dist/public/css/style.css
vendored
97
dist/public/css/style.css
vendored
@ -21,9 +21,9 @@ h1 {
|
|||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
background-color: #f3f3f3;
|
background-color: #F3F3F3;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,42 +43,35 @@ hr {
|
|||||||
height: 35px;
|
height: 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#addItem i {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Custom Styles */
|
/* Custom Styles */
|
||||||
|
|
||||||
@keyframes fadein {
|
@keyframes fadein {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity:0;
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity:1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@-moz-keyframes fadein {
|
@-moz-keyframes fadein { /* Firefox */
|
||||||
/* Firefox */
|
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity:0;
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity:1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@-webkit-keyframes fadein {
|
@-webkit-keyframes fadein { /* Safari and Chrome */
|
||||||
/* Safari and Chrome */
|
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity:0;
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity:1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@-o-keyframes fadein {
|
@-o-keyframes fadein { /* Opera */
|
||||||
/* Opera */
|
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity:0;
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -91,7 +84,7 @@ hr {
|
|||||||
|
|
||||||
.loader-fullscreen {
|
.loader-fullscreen {
|
||||||
background-color: #000;
|
background-color: #000;
|
||||||
background-color: rgba(0, 0, 0, 0.8);
|
background-color: rgba(0,0,0,.8);
|
||||||
position: fixed;
|
position: fixed;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -136,9 +129,7 @@ hr {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.block-title h1,
|
.block-title h1, .block-title h2, .block-title h3 {
|
||||||
.block-title h2,
|
|
||||||
.block-title h3 {
|
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,6 +170,7 @@ button#addItem {
|
|||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Cart */
|
/* Cart */
|
||||||
.cart-btn {
|
.cart-btn {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -220,7 +212,7 @@ button#addItem {
|
|||||||
|
|
||||||
.c_modal-wrap {
|
.c_modal-wrap {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
background: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, .5);
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -342,10 +334,10 @@ button#addItem {
|
|||||||
transition: all 0.4s;
|
transition: all 0.4s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-content__right input[type='number'] {
|
.modal-content__right input[type="number"] {
|
||||||
background: none;
|
background: none;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
border: 1px solid #eaeced;
|
border: 1px solid #EAECED;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
color: #404751;
|
color: #404751;
|
||||||
@ -359,11 +351,12 @@ button#addItem {
|
|||||||
color: #99a1aa;
|
color: #99a1aa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Information Block */
|
/* Information Block */
|
||||||
.information {
|
.information {
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
background-color: #ffe8e8;
|
background-color: #FFE8E8;
|
||||||
color: #333;
|
color: #333;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
@ -386,7 +379,7 @@ button#addItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.pagination li.active {
|
.pagination li.active {
|
||||||
background-color: #26a69a;
|
background-color: #26A69A;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Header */
|
/* Header */
|
||||||
@ -399,7 +392,7 @@ header.main-header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
background-color: #26a69a;
|
background-color: #26A69A;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,7 +440,7 @@ footer.main-footer .container {
|
|||||||
height: 42px;
|
height: 42px;
|
||||||
line-height: 42px;
|
line-height: 42px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: #efefef;
|
background: #EFEFEF;
|
||||||
color: #929292;
|
color: #929292;
|
||||||
margin: 3px;
|
margin: 3px;
|
||||||
transition: 0.2s;
|
transition: 0.2s;
|
||||||
@ -455,7 +448,7 @@ footer.main-footer .container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.category-items li a:hover {
|
.category-items li a:hover {
|
||||||
background: #26a69a;
|
background: #26A69A;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,7 +484,7 @@ footer.main-footer .container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.items-category {
|
.items-category {
|
||||||
background-color: #26a69a;
|
background-color: #26A69A;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -507,7 +500,7 @@ footer.main-footer .container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.items-list {
|
.items-list {
|
||||||
background-color: #f9f9f9;
|
background-color: #F9F9F9;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -560,7 +553,7 @@ footer.main-footer .container {
|
|||||||
.list-count,
|
.list-count,
|
||||||
.list-price {
|
.list-price {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
background: #e7e7e7;
|
background: #E7E7E7;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
min-width: 79px;
|
min-width: 79px;
|
||||||
@ -593,6 +586,7 @@ footer.main-footer .container {
|
|||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.main-section .background {
|
.main-section .background {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 40px;
|
padding: 40px;
|
||||||
@ -616,7 +610,7 @@ footer.main-footer .container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.product .breadcrumb li::after {
|
.product .breadcrumb li::after {
|
||||||
content: '/';
|
content: "/";
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
color: #b3b3b3;
|
color: #b3b3b3;
|
||||||
}
|
}
|
||||||
@ -669,7 +663,7 @@ footer.main-footer .container {
|
|||||||
.product-info__count span {
|
.product-info__count span {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
background: #e7e7e7;
|
background: #E7E7E7;
|
||||||
padding: 2px 13px;
|
padding: 2px 13px;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
color: #000;
|
color: #000;
|
||||||
@ -680,7 +674,7 @@ footer.main-footer .container {
|
|||||||
.product-info__price span {
|
.product-info__price span {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
background: #e7e7e7;
|
background: #E7E7E7;
|
||||||
padding: 2px 13px;
|
padding: 2px 13px;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
color: #000;
|
color: #000;
|
||||||
@ -696,7 +690,7 @@ footer.main-footer .container {
|
|||||||
|
|
||||||
/* Product-info Right */
|
/* Product-info Right */
|
||||||
.product-info__right {
|
.product-info__right {
|
||||||
background-color: #f9f9f9;
|
background-color: #F9F9F9;
|
||||||
min-height: 450px;
|
min-height: 450px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@ -763,9 +757,7 @@ footer.main-footer {
|
|||||||
.login-form__content button {
|
.login-form__content button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.sidenav .collapsible-header,
|
.sidenav .collapsible-header, .sidenav.fixed .collapsible-header, .sidenav li>a {
|
||||||
.sidenav.fixed .collapsible-header,
|
|
||||||
.sidenav li > a {
|
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
}
|
}
|
||||||
.nav-wrapper .brand-logo {
|
.nav-wrapper .brand-logo {
|
||||||
@ -798,15 +790,14 @@ footer.main-footer {
|
|||||||
.card .card-content h3 {
|
.card .card-content h3 {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
.table-buyers,
|
.table-buyers, .table-items {
|
||||||
.table-items {
|
|
||||||
max-height: 385px;
|
max-height: 385px;
|
||||||
}
|
}
|
||||||
#items .card-content {
|
#items .card-content {
|
||||||
min-height: 519px;
|
min-height: 519px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
footer.admin-footer {
|
footer.admin-footer{
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-top: 1px solid #e0e0e0;
|
border-top: 1px solid #e0e0e0;
|
||||||
color: #444;
|
color: #444;
|
||||||
@ -815,14 +806,14 @@ footer.admin-footer {
|
|||||||
}
|
}
|
||||||
.admin-footer ul li a {
|
.admin-footer ul li a {
|
||||||
color: #999;
|
color: #999;
|
||||||
font-size: 0.9rem;
|
font-size: .9rem;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
}
|
}
|
||||||
.admin-footer h5 {
|
.admin-footer h5 {
|
||||||
font-size: 1.64rem;
|
font-size: 1.64rem;
|
||||||
line-height: 110%;
|
line-height: 110%;
|
||||||
margin: 1.0933333333rem 0 0.656rem 0;
|
margin: 1.0933333333rem 0 .656rem 0;
|
||||||
}
|
}
|
||||||
.admin-footer ul li {
|
.admin-footer ul li {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
@ -842,23 +833,19 @@ footer.admin-footer {
|
|||||||
.modal .modal-content h4 {
|
.modal .modal-content h4 {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
div.table td,
|
div.table td, div.table th {
|
||||||
div.table th {
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
div.table > table > tbody > tr > td:nth-child(1) > img {
|
div.table > table > tbody > tr > td:nth-child(1) > img{
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
div.table > table > tbody > tr > td:nth-child(7) > button.btn.orange {
|
div.table > table > tbody > tr > td:nth-child(7) > button.btn.orange {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
.sidenav .collapsible-body > ul:not(.collapsible) > li.active,
|
.sidenav .collapsible-body>ul:not(.collapsible)>li.active, .sidenav.sidenav-fixed .collapsible-body>ul:not(.collapsible)>li.active {
|
||||||
.sidenav.sidenav-fixed .collapsible-body > ul:not(.collapsible) > li.active {
|
background-color: #4E978C;
|
||||||
background-color: #4e978c;
|
|
||||||
}
|
}
|
||||||
.sidenav .collapsible-body > ul:not(.collapsible) > li.active a,
|
.sidenav .collapsible-body>ul:not(.collapsible)>li.active a, .sidenav.sidenav-fixed .collapsible-body>ul:not(.collapsible)>li.active a, .sidenav.sidenav-fixed .collapsible-body>ul:not(.collapsible)>li.active i {
|
||||||
.sidenav.sidenav-fixed .collapsible-body > ul:not(.collapsible) > li.active a,
|
|
||||||
.sidenav.sidenav-fixed .collapsible-body > ul:not(.collapsible) > li.active i {
|
|
||||||
color: #f6f6f6;
|
color: #f6f6f6;
|
||||||
}
|
}
|
||||||
.card h5 {
|
.card h5 {
|
||||||
|
|||||||
11
dist/public/js/admin.js
vendored
11
dist/public/js/admin.js
vendored
File diff suppressed because one or more lines are too long
11
dist/public/js/main.js
vendored
11
dist/public/js/main.js
vendored
File diff suppressed because one or more lines are too long
3
dist/views/admin/assets/scripts.pug
vendored
3
dist/views/admin/assets/scripts.pug
vendored
@ -1,3 +1,2 @@
|
|||||||
script(async defer src="https://buttons.github.io/buttons.js")
|
script(async defer src="https://buttons.github.io/buttons.js")
|
||||||
//script(src='../js/admin.js')
|
script(src='../js/admin.js')
|
||||||
script(src="http://localhost:8084/admin.js")
|
|
||||||
3
dist/views/main/assets/scripts.pug
vendored
3
dist/views/main/assets/scripts.pug
vendored
@ -1,4 +1,3 @@
|
|||||||
// Script JS
|
// Script JS
|
||||||
//script(src='../js/main.js')
|
script(src='../js/main.js')
|
||||||
script(src='http://localhost:8084/main.js')
|
|
||||||
script(src="../js/disqus.js")
|
script(src="../js/disqus.js")
|
||||||
2
dist/views/main/contacts.pug
vendored
2
dist/views/main/contacts.pug
vendored
@ -19,7 +19,7 @@ main.main-section
|
|||||||
p
|
p
|
||||||
strong Контакты
|
strong Контакты
|
||||||
p
|
p
|
||||||
!= store.contacts
|
p(style="white-space: pre-wrap;") #{store.contacts}
|
||||||
|
|
||||||
|
|
||||||
//- Footer Template
|
//- Footer Template
|
||||||
|
|||||||
2
dist/views/main/how-to-buy.pug
vendored
2
dist/views/main/how-to-buy.pug
vendored
@ -19,7 +19,7 @@ main.main-section
|
|||||||
p
|
p
|
||||||
strong Для того, чтобы купить аккаунты в магазине, нужно проделать следующие шаги:
|
strong Для того, чтобы купить аккаунты в магазине, нужно проделать следующие шаги:
|
||||||
p
|
p
|
||||||
!= store.howToBuy
|
p(style="white-space: pre-wrap;") #{store.howToBuy}
|
||||||
|
|
||||||
|
|
||||||
//- Footer Template
|
//- Footer Template
|
||||||
|
|||||||
4
dist/views/main/index.pug
vendored
4
dist/views/main/index.pug
vendored
@ -16,7 +16,7 @@ main.main-section
|
|||||||
.information-title
|
.information-title
|
||||||
h2.small-title Важная информация:
|
h2.small-title Важная информация:
|
||||||
.information-text
|
.information-text
|
||||||
!= store.info
|
p(style="white-space: pre-wrap;") #{store.info}
|
||||||
.information-close
|
.information-close
|
||||||
button#information-close.waves-effect.btn.red Закрыть
|
button#information-close.waves-effect.btn.red Закрыть
|
||||||
// Information Block END
|
// Information Block END
|
||||||
@ -95,7 +95,7 @@ main.main-section
|
|||||||
.items-info__title.block-title
|
.items-info__title.block-title
|
||||||
i.material-icons info
|
i.material-icons info
|
||||||
h3.med-title Информация
|
h3.med-title Информация
|
||||||
!= store.infoBottom
|
p(style="white-space: pre-wrap;") #{store.infoBottom}
|
||||||
// Items Block END
|
// Items Block END
|
||||||
|
|
||||||
//- Footer Template
|
//- Footer Template
|
||||||
|
|||||||
8
dist/views/main/product.pug
vendored
8
dist/views/main/product.pug
vendored
@ -39,15 +39,15 @@ main.main-section
|
|||||||
.product-right__title
|
.product-right__title
|
||||||
span Описание:
|
span Описание:
|
||||||
hr
|
hr
|
||||||
!= description
|
p #{description}
|
||||||
.product-right__content
|
.product-right__content
|
||||||
h2 Тип аккаунтов #{category}
|
h2 Тип аккаунтов #{category}
|
||||||
!= type
|
p #{type}
|
||||||
h2 Формат аккаунтов #{category}
|
h2 Формат аккаунтов #{category}
|
||||||
!= format
|
p #{format}
|
||||||
hr
|
hr
|
||||||
h3 Рекомендации к покупке аккаунтов.
|
h3 Рекомендации к покупке аккаунтов.
|
||||||
!= store.recommendations
|
p #{store.recommendations}
|
||||||
|
|
||||||
h3 Отзывы о товаре
|
h3 Отзывы о товаре
|
||||||
#disqus_thread
|
#disqus_thread
|
||||||
|
|||||||
21
ecosystem.config.js
Normal file
21
ecosystem.config.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
module.exports = {
|
||||||
|
apps : [{
|
||||||
|
name: 'server',
|
||||||
|
script: 'server/server.js',
|
||||||
|
autorestart: true,
|
||||||
|
watch: true,
|
||||||
|
instances: 'max',
|
||||||
|
exec_mode: 'cluster',
|
||||||
|
env: {
|
||||||
|
NODE_ENV: 'development'
|
||||||
|
},
|
||||||
|
env_production: {
|
||||||
|
NODE_ENV: 'production',
|
||||||
|
DATABASE_URI: 'mongodb://127.0.0.1:27017',
|
||||||
|
DATABASE_NAME: '',
|
||||||
|
EMAIL_LOGIN: '',
|
||||||
|
EMAIL_PASSWORD: '',
|
||||||
|
QIWI_SECRET_KEY: ''
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
};
|
||||||
12
index.html
12
index.html
@ -1,12 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
||||||
<title>Webpack output javascript</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Webpack output javascript</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
4918
package-lock.json
generated
4918
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "digital-store",
|
"name": "digital-store",
|
||||||
"version": "1.0.0",
|
"version": "1.1.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "server/server.js",
|
"main": "server/server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node server/server.js",
|
"start": "node server/server.js"
|
||||||
"build": "webpack --mode production",
|
|
||||||
"wp-server": "webpack-dev-server --mode development"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -29,7 +27,6 @@
|
|||||||
"express-flash": "0.0.2",
|
"express-flash": "0.0.2",
|
||||||
"express-session": "^1.17.0",
|
"express-session": "^1.17.0",
|
||||||
"log4js": "^6.1.0",
|
"log4js": "^6.1.0",
|
||||||
"marked": "^1.1.0",
|
|
||||||
"method-override": "^3.0.0",
|
"method-override": "^3.0.0",
|
||||||
"mongodb": "^3.3.4",
|
"mongodb": "^3.3.4",
|
||||||
"multer": "^1.4.2",
|
"multer": "^1.4.2",
|
||||||
|
|||||||
@ -1,178 +1,162 @@
|
|||||||
const ObjectID = require('mongodb').ObjectID
|
const ObjectID = require('mongodb').ObjectID;
|
||||||
const marked = require('marked')
|
const nodemailer = require('../config/nodemailer.config');
|
||||||
const nodemailer = require('../config/nodemailer.config')
|
|
||||||
|
|
||||||
module.exports = function (server, db) {
|
module.exports = function (server, db) {
|
||||||
server.get('/', async (req, res) => {
|
server.get('/', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const store = await db.collection('content').findOne({})
|
const store = await db.collection('content').findOne({});
|
||||||
db.collection('categories')
|
db.collection('categories').find().toArray((err, result) => {
|
||||||
.find()
|
const categories = result.map(category => category.title[0].toUpperCase() + category.title.slice(1));
|
||||||
.toArray((err, result) => {
|
|
||||||
const categories = result.map(
|
|
||||||
(category) =>
|
|
||||||
category.title[0].toUpperCase() + category.title.slice(1)
|
|
||||||
)
|
|
||||||
res.render('main/index', {
|
res.render('main/index', {
|
||||||
pageName: 'index',
|
pageName: 'index',
|
||||||
title: `${store.title} | Главная`,
|
title: `${store.title} | Главная`,
|
||||||
categories,
|
categories,
|
||||||
store: {
|
store: {
|
||||||
title: store.title,
|
title: store.title,
|
||||||
info: marked(store.info),
|
info: store.info,
|
||||||
infoBottom: marked(store.infoBottom),
|
infoBottom: store.infoBottom,
|
||||||
color: store.color,
|
color: store.color
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} catch {
|
|
||||||
res.status(500).render('main/404')
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
res.status(500).render('main/404');
|
||||||
|
}
|
||||||
|
});
|
||||||
server.get('/how-to-buy', async (req, res) => {
|
server.get('/how-to-buy', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const store = await db.collection('content').findOne({})
|
const store = await db.collection('content').findOne({});
|
||||||
res.render('main/how-to-buy', {
|
res.render('main/how-to-buy', {
|
||||||
pageName: 'how-to-buy',
|
pageName: 'how-to-buy',
|
||||||
title: `${store.title} | Как купить товар`,
|
title: `${store.title} | Как купить товар`,
|
||||||
store: {
|
store: {
|
||||||
title: store.title,
|
title: store.title,
|
||||||
howToBuy: marked(store.howToBuy),
|
howToBuy: store.howToBuy,
|
||||||
color: store.color,
|
color: store.color
|
||||||
},
|
|
||||||
})
|
|
||||||
} catch {
|
|
||||||
res.status(500).render('main/404')
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
} catch {
|
||||||
|
res.status(500).render('main/404');
|
||||||
|
}
|
||||||
|
});
|
||||||
server.get('/contacts', async (req, res) => {
|
server.get('/contacts', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const store = await db.collection('content').findOne({})
|
const store = await db.collection('content').findOne({});
|
||||||
res.render('main/contacts', {
|
res.render('main/contacts', {
|
||||||
pageName: 'contacts',
|
pageName: 'contacts',
|
||||||
title: `${store.title} | Контакты`,
|
title: `${store.title} | Контакты`,
|
||||||
store: {
|
store: {
|
||||||
title: store.title,
|
title: store.title,
|
||||||
contacts: marked(store.contacts),
|
contacts: store.contacts,
|
||||||
color: store.color,
|
color: store.color
|
||||||
},
|
|
||||||
})
|
|
||||||
} catch {
|
|
||||||
res.status(500).render('main/404')
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
} catch {
|
||||||
|
res.status(500).render('main/404');
|
||||||
|
}
|
||||||
|
});
|
||||||
server.get('/comments', async (req, res) => {
|
server.get('/comments', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const store = await db.collection('content').findOne({})
|
const store = await db.collection('content').findOne({});
|
||||||
res.render('main/comments', {
|
res.render('main/comments', {
|
||||||
pageName: 'comments',
|
pageName: 'comments',
|
||||||
title: `${store.title} | Отзывы`,
|
title: `${store.title} | Отзывы`,
|
||||||
store: {
|
store: {
|
||||||
title: store.title,
|
title: store.title,
|
||||||
color: store.color,
|
color: store.color
|
||||||
},
|
|
||||||
})
|
|
||||||
} catch {
|
|
||||||
res.status(500).render('main/404')
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
} catch {
|
||||||
|
res.status(500).render('main/404');
|
||||||
|
}
|
||||||
|
});
|
||||||
server.get('/my-orders', async (req, res) => {
|
server.get('/my-orders', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const store = await db.collection('content').findOne({})
|
const store = await db.collection('content').findOne({});
|
||||||
res.render('main/my-orders', {
|
res.render('main/my-orders', {
|
||||||
pageName: 'my-orders',
|
pageName: 'my-orders',
|
||||||
title: `${store.title} | Мои покупки`,
|
title: `${store.title} | Мои покупки`,
|
||||||
store: {
|
store: {
|
||||||
title: store.title,
|
title: store.title,
|
||||||
color: store.color,
|
color: store.color
|
||||||
},
|
|
||||||
})
|
|
||||||
} catch {
|
|
||||||
res.status(500).render('main/404')
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
} catch {
|
||||||
|
res.status(500).render('main/404');
|
||||||
|
}
|
||||||
|
});
|
||||||
server.get('/product/:id', async (req, res) => {
|
server.get('/product/:id', async (req, res) => {
|
||||||
const store = await db.collection('content').findOne({})
|
const store = await db.collection('content').findOne({});
|
||||||
const product = new Promise((resolve, reject) => {
|
const product = new Promise((resolve, reject) => {
|
||||||
db.collection('items')
|
db.collection('items').find({_id: ObjectID(req.params.id)}).toArray()
|
||||||
.find({ _id: ObjectID(req.params.id) })
|
|
||||||
.toArray()
|
|
||||||
.then((items) => {
|
.then((items) => {
|
||||||
const item = items.find((item) => item)
|
const item = items.find(item => item);
|
||||||
resolve(item)
|
resolve(item);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
product
|
product.then((item) => {
|
||||||
.then((item) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
db.collection('categories')
|
db.collection('categories').find({title: item.category}).toArray()
|
||||||
.find({ title: item.category })
|
|
||||||
.toArray()
|
|
||||||
.then((categories) => {
|
.then((categories) => {
|
||||||
const category = categories.find((category) => category)
|
const category = categories.find(category => category);
|
||||||
resolve({ item, category })
|
resolve({item, category});
|
||||||
|
});
|
||||||
|
});
|
||||||
})
|
})
|
||||||
})
|
.then(((data) => {
|
||||||
})
|
|
||||||
.then((data) => {
|
|
||||||
const item = data.item,
|
const item = data.item,
|
||||||
category = data.category
|
category = data.category;
|
||||||
res.render('main/product', {
|
res.render('main/product', {
|
||||||
title: item.title,
|
title: item.title,
|
||||||
image: category.img,
|
image: category.img,
|
||||||
count: item.count,
|
count: item.count,
|
||||||
category: item.category[0].toUpperCase() + item.category.slice(1),
|
category: item.category[0].toUpperCase() + item.category.slice(1),
|
||||||
price: item.price,
|
price: item.price,
|
||||||
description: marked(item.description),
|
description: item.description,
|
||||||
date: item.date,
|
date: item.date,
|
||||||
id: item._id,
|
id: item._id,
|
||||||
type: marked(category.type),
|
type: category.type,
|
||||||
format: marked(category.format),
|
format: category.format,
|
||||||
store: {
|
store: {
|
||||||
title: store.title,
|
title: store.title,
|
||||||
recommendations: marked(store.recommendations),
|
recommendations: store.recommendations,
|
||||||
color: store.color,
|
color: store.color
|
||||||
},
|
}
|
||||||
})
|
});
|
||||||
})
|
}))
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
res.status(500).render('main/404')
|
res.status(500).render('main/404');
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
server.post('/my-orders', async (req, res) => {
|
server.post('/my-orders', async (req, res) => {
|
||||||
const buyers = await db
|
const buyers = await db.collection('buyers').find({email: req.body.email}).toArray();
|
||||||
.collection('buyers')
|
|
||||||
.find({ email: req.body.email })
|
|
||||||
.toArray()
|
|
||||||
if (buyers.length > 0) {
|
if (buyers.length > 0) {
|
||||||
// рендерим html
|
// рендерим html
|
||||||
const html = buyers.map((buyer) => {
|
const html = buyers.map((buyer) => {
|
||||||
return `
|
return `
|
||||||
<b>Номер заказа: ${buyer.bill_id}</b><br>
|
<b>Номер заказа: ${buyer.bill_id}</b><br>
|
||||||
<b>Дата оплаты: ${new Date(
|
<b>Дата оплаты: ${new Date(buyer.date).toLocaleDateString()}</b><br>
|
||||||
buyer.date
|
|
||||||
).toLocaleDateString()}</b><br>
|
|
||||||
<b>Данные товара:</b><br>
|
<b>Данные товара:</b><br>
|
||||||
<p>${buyer.data.join('<br>')}</p>
|
<p>${buyer.data.join('<br>')}</p>
|
||||||
<p>======================================================================</p>
|
<p>======================================================================</p>
|
||||||
`
|
`;
|
||||||
})
|
});
|
||||||
|
|
||||||
// высылаем данные покупателю
|
// высылаем данные покупателю
|
||||||
try {
|
try {
|
||||||
await nodemailer.transporter.sendMail({
|
await nodemailer.transporter.sendMail({
|
||||||
from: `"Ваши покупки." <${process.env.EMAIL_LOGIN}>`,
|
from: `"Ваши покупки." <${process.env.EMAIL_LOGIN}>`,
|
||||||
to: req.body.email,
|
to: req.body.email,
|
||||||
subject: 'Данные купленных товаров находятся в этом письме.',
|
subject: "Данные купленных товаров находятся в этом письме.",
|
||||||
html: html.join(''),
|
html: html.join('')
|
||||||
})
|
});
|
||||||
res.json({ status: 'OK' })
|
res.json({status: 'OK'});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.json({ status: 'ERR' })
|
res.json({status: 'ERR'});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res.json({ status: 'NF' })
|
res.json({status: 'NF'});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +0,0 @@
|
|||||||
import { Component } from '../core/component';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
|
|
||||||
export class AdministratorComponent extends Component {
|
|
||||||
constructor(id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
setElems() {
|
|
||||||
this.$el.querySelector('form').addEventListener('submit', this.request.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
async request(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
try {
|
|
||||||
const data = {
|
|
||||||
id: e.target.elements.id.value,
|
|
||||||
name: e.target.elements.name.value,
|
|
||||||
email: e.target.elements.email.value,
|
|
||||||
oldPass: e.target.elements.oldPass.value,
|
|
||||||
newPass: e.target.elements.newPass.value
|
|
||||||
}
|
|
||||||
const response = await axios.post('/control-panel/administrators/update', data);
|
|
||||||
alert(response.data);
|
|
||||||
document.location.reload(true);
|
|
||||||
} catch (error) {
|
|
||||||
alert('Невозможно выполнить запрос.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
import { apiService } from "../services/api.service";
|
|
||||||
import { renderCategories } from "../templates/categories.template";
|
|
||||||
import { renderIcons } from "../templates/icons.template";
|
|
||||||
|
|
||||||
export class CategoriesComponent extends Component {
|
|
||||||
constructor(id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
setElems() {
|
|
||||||
// селекты в которых рендерится контент
|
|
||||||
this.$editSelect = this.$el.querySelector('#editCategory_select'); // редактирование категории
|
|
||||||
this.$dellSelect = this.$el.querySelector('#deleteCategory_select'); // удаление категории
|
|
||||||
this.$iconsSelect = this.$el.querySelector('#icons_select'); // выбор иконки
|
|
||||||
|
|
||||||
// обработка форм
|
|
||||||
this.$addForm = this.$el.querySelector('#addCategory');
|
|
||||||
this.$dellForm = this.$el.querySelector('#deleteCategory');
|
|
||||||
|
|
||||||
// добавим ивенты на наши формы
|
|
||||||
this.$addForm.addEventListener('submit', this.addCategory.bind(this));
|
|
||||||
this.$dellForm.addEventListener('submit', this.deleteCategory.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
const categories = await apiService.getCategories(),
|
|
||||||
icons = await apiService.getIcons();
|
|
||||||
|
|
||||||
this.$editSelect.insertAdjacentHTML('beforeend', renderCategories(categories));
|
|
||||||
this.$dellSelect.insertAdjacentHTML('beforeend', renderCategories(categories));
|
|
||||||
this.$iconsSelect.insertAdjacentHTML('beforeend', renderIcons(icons));
|
|
||||||
M.FormSelect.init(document.querySelectorAll('select'));
|
|
||||||
}
|
|
||||||
|
|
||||||
async addCategory(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const categories = await apiService.getCategories(),
|
|
||||||
title = this.$addForm.elements[0].value.toLowerCase().trim(),
|
|
||||||
index = categories.map((category) => {return category.title}).indexOf(title);
|
|
||||||
|
|
||||||
index != -1 ? alert('Такая категория уже существует, придумайте другое название.') : this.$addForm.submit();
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteCategory(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const categories = await apiService.getCategories(),
|
|
||||||
title = this.$dellForm.elements[0].value.toLowerCase().trim(),
|
|
||||||
index = categories.map((category) => {return category.title}).indexOf(title);
|
|
||||||
|
|
||||||
index != -1 ? this.$dellForm.submit() : alert('Выберите категорию для удаления.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
import { Component } from '../core/component';
|
|
||||||
|
|
||||||
export class ContentComponent extends Component {
|
|
||||||
constructor(id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
setElems() {
|
|
||||||
this.$info = this.$el.querySelector('#info');
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
M.textareaAutoResize(this.$info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
import { apiService } from "../services/api.service";
|
|
||||||
import { renderBuyers } from "../templates/buyers.template";
|
|
||||||
|
|
||||||
export class DashboardComponent extends Component {
|
|
||||||
constructor(id, {pagination}, {search}) {
|
|
||||||
super(id);
|
|
||||||
this.pagination = pagination;
|
|
||||||
this.search = search;
|
|
||||||
}
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
const buyers = await apiService.getBuyers();
|
|
||||||
this.$table = this.$el.querySelector('table tbody');
|
|
||||||
this.search.initialize(this.$table, this.getBuyersByBillId);
|
|
||||||
this.pagination.initialize(buyers.length, this.$table, this.getBuyersByPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getBuyersByBillId(bill_id) {
|
|
||||||
const buyer = await apiService.getBillId(bill_id);
|
|
||||||
return renderBuyers(buyer);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getBuyersByPage(page) {
|
|
||||||
const buyers = await apiService.getBuyersOffset(page);
|
|
||||||
return renderBuyers(buyers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
import { apiService } from "../services/api.service";
|
|
||||||
import { renderIcons } from "../templates/icons.template";
|
|
||||||
|
|
||||||
export class IconsComponent extends Component {
|
|
||||||
constructor(id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
setElems() {
|
|
||||||
this.$icons = this.$el.querySelector('#selectIcons');
|
|
||||||
this.$delete = this.$el.querySelector('#dellIcon');
|
|
||||||
this.$delete.addEventListener('submit', this.dellIcon.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
const icons = await apiService.getIcons();
|
|
||||||
this.$icons.insertAdjacentHTML('beforeend', renderIcons(icons));
|
|
||||||
M.FormSelect.init(this.$icons);
|
|
||||||
}
|
|
||||||
|
|
||||||
async dellIcon(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const icons = await apiService.getIcons(),
|
|
||||||
currIcon = e.target.elements[0].value,
|
|
||||||
index = icons.indexOf(currIcon);
|
|
||||||
|
|
||||||
index != -1 ? e.target.submit() : alert('Выберите иконку для удаления.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
import { apiService } from "../services/api.service";
|
|
||||||
import { renderItems } from "../templates/items.template";
|
|
||||||
import { renderCategories } from "../templates/categories.template";
|
|
||||||
|
|
||||||
export class ItemsComponent extends Component {
|
|
||||||
constructor(id, {pagination}, {search}) {
|
|
||||||
super(id);
|
|
||||||
this.pagination = pagination;
|
|
||||||
this.search = search;
|
|
||||||
}
|
|
||||||
|
|
||||||
setElems() {
|
|
||||||
this.$categories = this.$el.querySelector('#selectCategories');
|
|
||||||
}
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
const items = await apiService.getItems(),
|
|
||||||
categories = await apiService.getCategories();
|
|
||||||
|
|
||||||
this.$table = this.$el.querySelector('table tbody');
|
|
||||||
this.search.initialize(this.$table, this.getItemsByText);
|
|
||||||
this.pagination.initialize(items.length, this.$table, this.getItemsByPage);
|
|
||||||
this.$categories.insertAdjacentHTML('beforeend', renderCategories(categories));
|
|
||||||
M.FormSelect.init(this.$categories);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getItemsByText(text) {
|
|
||||||
const items = await apiService.getItemsQuery(text),
|
|
||||||
categories = await apiService.getCategories(),
|
|
||||||
data = await apiService.getItemsData();
|
|
||||||
return renderItems(items, categories, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getItemsByPage(page) {
|
|
||||||
const items = await apiService.getItemsOffset(page),
|
|
||||||
categories = await apiService.getCategories(),
|
|
||||||
data = await apiService.getItemsData();
|
|
||||||
return renderItems(items, categories, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
|
|
||||||
export class LoaderComponent extends Component {
|
|
||||||
constructor(id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,135 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
|
|
||||||
export class PaginationComponent extends Component {
|
|
||||||
constructor(id, {loader}) {
|
|
||||||
super(id);
|
|
||||||
this.loader = loader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {number} length - кол-во товаров в магазине
|
|
||||||
* @param {object} body - куда вставляем данные о первой странице
|
|
||||||
* @param {function} request - функция async запроса
|
|
||||||
*/
|
|
||||||
|
|
||||||
initialize(length, body, request) {
|
|
||||||
// рендерим кнопку с первой страницой как независимую от других кнопок
|
|
||||||
this.$el.insertAdjacentHTML('afterbegin', '<li><a href="#" data-move="1">1</a></li><li><span style="position: relative;top: 4px;padding: 4px;">...</span></li>');
|
|
||||||
|
|
||||||
// рендерим сами кнопки и кнопки вперед - назад
|
|
||||||
this.renderBtns(length);
|
|
||||||
this.renderArrows();
|
|
||||||
|
|
||||||
// показ первой страницы
|
|
||||||
this.move(1, body, request);
|
|
||||||
|
|
||||||
// назначаем ивент на кнопки
|
|
||||||
this.$el.onclick = (e) => this.buttonHandler(e, body, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
// страницу назад - вперед
|
|
||||||
renderArrows() {
|
|
||||||
const leftArrowHTML = '<li><a class="disabled" href="#" data-type="prev"><i class="material-icons">chevron_left</i></a></li>',
|
|
||||||
rightArrowHTML = '<li><a class="disabled" href="#" data-type="next"><i class="material-icons">chevron_right</i></a></li>';
|
|
||||||
|
|
||||||
this.$el.insertAdjacentHTML('afterbegin', leftArrowHTML);
|
|
||||||
this.$el.insertAdjacentHTML('beforeend', rightArrowHTML);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderBtns(length) {
|
|
||||||
let count = Math.ceil(length / 5),
|
|
||||||
btnsHTML = '';
|
|
||||||
|
|
||||||
// проверяем - если товаров > 21, рендерим максимум до пяти кнопок
|
|
||||||
length > 21 ? count = 5 : count;
|
|
||||||
|
|
||||||
this.count = Math.ceil(length / 5);
|
|
||||||
for (let i = 2; i <= count; i++) {
|
|
||||||
btnsHTML += `<li><a href="#" data-move="${i}">${i}</a></li>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$el.insertAdjacentHTML('beforeend', btnsHTML);
|
|
||||||
}
|
|
||||||
|
|
||||||
async move(page, body, request) {
|
|
||||||
this.loader.show();
|
|
||||||
const $btns = Array.from(this.$el.querySelectorAll('li a'));
|
|
||||||
|
|
||||||
if (page === +$btns[$btns.length - 2].dataset.move && page < this.count) {
|
|
||||||
const numbers = $btns.filter(btn => btn.dataset.move);
|
|
||||||
|
|
||||||
for (let i = 1; i < numbers.length; i++) {
|
|
||||||
let newIndex = numbers[i].textContent;
|
|
||||||
++newIndex;
|
|
||||||
numbers[i].setAttribute('data-move', newIndex);
|
|
||||||
numbers[i].textContent = newIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page === +$btns[2].dataset.move && page > 2) {
|
|
||||||
const numbers = $btns.filter(btn => btn.dataset.move);
|
|
||||||
|
|
||||||
for (let i = 1; i < numbers.length; i++) {
|
|
||||||
let newIndex = numbers[i].textContent;
|
|
||||||
--newIndex;
|
|
||||||
numbers[i].setAttribute('data-move', newIndex);
|
|
||||||
numbers[i].textContent = newIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page <= this.count) {
|
|
||||||
// красим кнопки
|
|
||||||
const numbers = $btns.filter(btn => btn.dataset.move);
|
|
||||||
numbers.forEach(btn => btn.parentNode.classList.remove('active'));
|
|
||||||
numbers.forEach(btn => {
|
|
||||||
if (page === +btn.dataset.move) {
|
|
||||||
btn.parentNode.classList.add('active');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const response = await request(page);
|
|
||||||
body.innerHTML = '';
|
|
||||||
if (response != '') {
|
|
||||||
this.$el.setAttribute('data-page', page);
|
|
||||||
body.insertAdjacentHTML('afterbegin', response);
|
|
||||||
} else body.innerHTML = 'Данных нету.';
|
|
||||||
}
|
|
||||||
this.loader.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
buttonHandler(e, body, request) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (e.target.dataset.move) {
|
|
||||||
const page = parseInt(e.target.dataset.move),
|
|
||||||
target = e.target.parentNode;
|
|
||||||
|
|
||||||
this.move(page, body, request);
|
|
||||||
} else if (e.target.tagName.toLowerCase() === 'i') {
|
|
||||||
const type = e.target.parentNode.dataset.type,
|
|
||||||
page = parseInt(this.$el.dataset.page);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 'next':
|
|
||||||
this.next(page, body, request);
|
|
||||||
break;
|
|
||||||
case 'prev':
|
|
||||||
this.prev(page, body, request);
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next(page, body, request) {
|
|
||||||
this.move(++page, body, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
prev(page, body, request) {
|
|
||||||
--page;
|
|
||||||
if (page < 1) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
this.move(page, body, request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
|
|
||||||
export class SearchComponent extends Component {
|
|
||||||
constructor(id, {loader}) {
|
|
||||||
super(id);
|
|
||||||
this.loader = loader;
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize(insertTo, request) {
|
|
||||||
this.$el.onchange = (e) => {
|
|
||||||
this.search(e, insertTo, request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async search(e, insertTo, request) {
|
|
||||||
try {
|
|
||||||
insertTo.innerHTML = '';
|
|
||||||
this.loader.show();
|
|
||||||
const query = e.target.value.trim(),
|
|
||||||
items = await request(query);
|
|
||||||
|
|
||||||
items != '' ? insertTo.insertAdjacentHTML('afterbegin', items) : insertTo.innerHTML = `По запросу "${query}" ничего не найдено...`;
|
|
||||||
e.target.value = '';
|
|
||||||
this.loader.hide();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Произошла ошибка.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
const M = require('../frameworks/materialize.min');
|
|
||||||
export class MaterializeConfig {
|
|
||||||
constructor() {
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
// Closest полифилл для ie11
|
|
||||||
(function() {
|
|
||||||
if (!Element.prototype.closest) {
|
|
||||||
Element.prototype.closest = function(css) {
|
|
||||||
var node = this;
|
|
||||||
|
|
||||||
while (node) {
|
|
||||||
if (node.matches(css)) return node;
|
|
||||||
else node = node.parentElement;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
// Matches полифилл для ie11
|
|
||||||
(function() {
|
|
||||||
if (!Element.prototype.matches) {
|
|
||||||
Element.prototype.matches = Element.prototype.matchesSelector ||
|
|
||||||
Element.prototype.webkitMatchesSelector ||
|
|
||||||
Element.prototype.mozMatchesSelector ||
|
|
||||||
Element.prototype.msMatchesSelector;
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
||||||
M.Sidenav.init(document.querySelectorAll('.sidenav'));
|
|
||||||
M.Collapsible.init(document.querySelectorAll('.collapsible'));
|
|
||||||
M.Modal.init(document.querySelectorAll('.modal'));
|
|
||||||
document.querySelector('#type') ? M.textareaAutoResize(document.querySelector('#type')) : false
|
|
||||||
M.updateTextFields();
|
|
||||||
|
|
||||||
// окраска li по нажатию
|
|
||||||
if (document.querySelector('#currentPage')) {
|
|
||||||
const currentPage = document.querySelector('#currentPage').dataset.current;
|
|
||||||
const pages = Array.from(document.querySelectorAll('.collapsible li'));
|
|
||||||
pages.forEach(function (page) {
|
|
||||||
if (page.dataset.page) {
|
|
||||||
if (currentPage === page.dataset.page) {
|
|
||||||
document.querySelector('#home').classList.remove('active');
|
|
||||||
page.classList.add('active');
|
|
||||||
const collapse = M.Collapsible.getInstance(page.closest('.collapsible'));
|
|
||||||
collapse.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
export class Component {
|
|
||||||
constructor(id) {
|
|
||||||
this.$el = document.getElementById(id);
|
|
||||||
this.$el ? this.setElems() : false;
|
|
||||||
this.$el ? this.init() : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
setElems() {}
|
|
||||||
|
|
||||||
onShow() {}
|
|
||||||
|
|
||||||
onHide() {}
|
|
||||||
|
|
||||||
hide() {
|
|
||||||
this.$el ? this.$el.classList.add('hide') : false;
|
|
||||||
this.onHide();
|
|
||||||
}
|
|
||||||
|
|
||||||
show() {
|
|
||||||
this.$el ? this.$el.classList.remove('hide') : false;
|
|
||||||
this.onShow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
export class Modal {
|
|
||||||
constructor(id, open, close) {
|
|
||||||
this.$el = document.getElementById(id);
|
|
||||||
this.open = document.getElementById(open);
|
|
||||||
this.close = document.getElementById(close);
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
if (this.open) {
|
|
||||||
this.setElems();
|
|
||||||
this.open.addEventListener('click', this.show.bind(this));
|
|
||||||
this.close.addEventListener('click', this.hide.bind(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
show(e) {
|
|
||||||
this.$el.classList.remove('hide');
|
|
||||||
document.body.style.overflowY = 'hidden';
|
|
||||||
this.openHandler(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
hide() {
|
|
||||||
this.$el.classList.add('hide');
|
|
||||||
document.body.style.overflowY = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
openHandler() {}
|
|
||||||
|
|
||||||
setElems() {}
|
|
||||||
}
|
|
||||||
6
src/admin/frameworks/materialize.min.js
vendored
6
src/admin/frameworks/materialize.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,34 +0,0 @@
|
|||||||
import { MaterializeConfig } from "./config/materialize.config.js";
|
|
||||||
import { CategoriesComponent } from "./components/categories.component.js";
|
|
||||||
import { ItemsComponent } from "./components/items.component.js";
|
|
||||||
import { LoaderComponent } from "./components/loader.component.js";
|
|
||||||
import { PaginationComponent } from "./components/pagination.component.js";
|
|
||||||
import { SearchComponent } from "./components/search.component.js";
|
|
||||||
import { IconsComponent } from "./components/icons.component.js";
|
|
||||||
import { ItemEditModal } from "./modals/item-edit.modal.js";
|
|
||||||
import { ItemDeleteModal } from "./modals/item-delete.modal.js";
|
|
||||||
import { CategoryEditModal } from "./modals/category-edit.modal";
|
|
||||||
import { ContentComponent } from "./components/content.component.js";
|
|
||||||
import { AdministratorComponent } from "./components/administrator.component.js";
|
|
||||||
import { DashboardComponent } from "./components/dashboard.component.js";
|
|
||||||
import { BillDataModal } from "./modals/bill-data.modal.js";
|
|
||||||
|
|
||||||
// Components
|
|
||||||
const loader = new LoaderComponent('loader');
|
|
||||||
const pagination = new PaginationComponent('pagination', {loader});
|
|
||||||
const search = new SearchComponent('search', {loader});
|
|
||||||
const categories = new CategoriesComponent('categories');
|
|
||||||
const icons = new IconsComponent('icons');
|
|
||||||
const items = new ItemsComponent('items', {pagination}, {search});
|
|
||||||
const dashboards = new DashboardComponent('dashboard', {pagination}, {search});
|
|
||||||
const content = new ContentComponent('content');
|
|
||||||
const administrator = new AdministratorComponent('administrator');
|
|
||||||
|
|
||||||
// Modals
|
|
||||||
const itemEdit = new ItemEditModal('editItemModal', 'edit', 'editItemClose', {loader});
|
|
||||||
const itemDelete = new ItemDeleteModal('deleteItemModal', 'edit', 'deleteItemClose');
|
|
||||||
const categoryEdit = new CategoryEditModal('editCategoryModal', 'editCategory_select', 'editCategoryClose', {loader});
|
|
||||||
const billData = new BillDataModal('billDataModal', 'dashboard', 'billDataClose', {loader});
|
|
||||||
|
|
||||||
const materialize = new MaterializeConfig();
|
|
||||||
console.log('Control-Panel v1.0.0');
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
import { Modal } from "../core/modal";
|
|
||||||
import { apiService } from "../services/api.service";
|
|
||||||
|
|
||||||
export class BillDataModal extends Modal {
|
|
||||||
constructor(id, open, close, {loader}) {
|
|
||||||
super(id, open, close);
|
|
||||||
this.loader = loader
|
|
||||||
}
|
|
||||||
|
|
||||||
show(e) {
|
|
||||||
if (e.target.id === 'billDataOpen') {
|
|
||||||
this.$el.classList.remove('hide');
|
|
||||||
document.body.style.overflowY = 'hidden';
|
|
||||||
this.openHandler(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setElems() {
|
|
||||||
this.$bill_id = this.$el.querySelector('#bill_id');
|
|
||||||
this.$email = this.$el.querySelector('#email');
|
|
||||||
this.$method = this.$el.querySelector('#method');
|
|
||||||
this.$date = this.$el.querySelector('#date');
|
|
||||||
this.$amount = this.$el.querySelector('#amount');
|
|
||||||
this.$data = this.$el.querySelector('#data');
|
|
||||||
}
|
|
||||||
|
|
||||||
async openHandler(e) {
|
|
||||||
try {
|
|
||||||
this.loader.show();
|
|
||||||
const buyer = await apiService.getBuyerById(e.target.parentNode.parentNode.dataset.id)
|
|
||||||
|
|
||||||
this.$bill_id.value = buyer.bill_id;
|
|
||||||
this.$email.value = buyer.email;
|
|
||||||
this.$method.value = buyer.method[0].toUpperCase() + buyer.method.slice(1);
|
|
||||||
this.$date.value = buyer.date;
|
|
||||||
this.$amount.value = buyer.amount;
|
|
||||||
this.$data.textContent = buyer.data.join('\n');
|
|
||||||
|
|
||||||
M.textareaAutoResize(this.$data);
|
|
||||||
M.updateTextFields();
|
|
||||||
this.loader.hide();
|
|
||||||
} catch (error) {
|
|
||||||
alert('Произошла ошибка, обновите страницу')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
import { Modal } from "../core/modal";
|
|
||||||
import { apiService } from "../services/api.service";
|
|
||||||
import { renderIcons } from "../templates/icons.template";
|
|
||||||
|
|
||||||
export class CategoryEditModal extends Modal {
|
|
||||||
constructor(id, open, close, {loader}) {
|
|
||||||
super(id, open, close);
|
|
||||||
this.loader = loader;
|
|
||||||
}
|
|
||||||
|
|
||||||
// перепишем метод для перевода ивента open в событие onchange
|
|
||||||
init() {
|
|
||||||
if (this.open) {
|
|
||||||
this.setElems();
|
|
||||||
this.open.addEventListener('change', this.show.bind(this));
|
|
||||||
this.close.addEventListener('click', this.hide.bind(this));
|
|
||||||
this.$el.querySelector('form').addEventListener('submit', this.updateCategory.bind(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setElems() {
|
|
||||||
this.$id = this.$el.querySelector('.id');
|
|
||||||
this.$originalTitle = this.$el.querySelector('.originalTitle');
|
|
||||||
this.$title = this.$el.querySelector('.title');
|
|
||||||
this.$type = this.$el.querySelector('.type');
|
|
||||||
this.$format = this.$el.querySelector('.format');
|
|
||||||
this.$img = this.$el.querySelector('.img');
|
|
||||||
}
|
|
||||||
|
|
||||||
async openHandler(e) {
|
|
||||||
try {
|
|
||||||
this.loader.show();
|
|
||||||
const icons = await apiService.getIcons(),
|
|
||||||
category = await apiService.getCategoryById(e.target.value)
|
|
||||||
|
|
||||||
this.$img.innerHTML = `<option value='${category.img}' selected>Оставить прежнюю</option>`;
|
|
||||||
this.$id.value = category._id;
|
|
||||||
this.$originalTitle.value = category.title[0].toUpperCase() + category.title.slice(1);
|
|
||||||
this.$title.value = category.title[0].toUpperCase() + category.title.slice(1);
|
|
||||||
this.$type.value = category.type;
|
|
||||||
this.$format.value = category.format;
|
|
||||||
this.$img.insertAdjacentHTML('beforeend', renderIcons(icons));
|
|
||||||
M.FormSelect.init(this.$img);
|
|
||||||
M.textareaAutoResize(this.$type);
|
|
||||||
M.updateTextFields();
|
|
||||||
this.loader.hide();
|
|
||||||
} catch (error) {
|
|
||||||
alert('Произошла ошибка, обновите страницу')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateCategory(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const form = this.$el.querySelector('form');
|
|
||||||
const categories = await apiService.getCategories();
|
|
||||||
const id = form.elements[0].value.toLowerCase().trim();
|
|
||||||
const index = categories.map((category) => {return category._id}).indexOf(id);
|
|
||||||
index != -1 ? form.submit() : alert('Категории с таким ID не существует.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
import { Modal } from "../core/modal";
|
|
||||||
import { apiService } from "../services/api.service";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
export class ItemDeleteModal extends Modal {
|
|
||||||
constructor(id, open, close) {
|
|
||||||
super(id, open, close);
|
|
||||||
}
|
|
||||||
|
|
||||||
show(e) {
|
|
||||||
if (e.target.id === 'deleteItem') {
|
|
||||||
this.$el.classList.remove('hide');
|
|
||||||
document.body.style.overflowY = 'hidden';
|
|
||||||
this.openHandler(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setElems() {
|
|
||||||
this.$content = this.$el.querySelector('.modal-content__form p');
|
|
||||||
this.$button = this.$el.querySelector('.delete-item');
|
|
||||||
}
|
|
||||||
|
|
||||||
async openHandler(e) {
|
|
||||||
try {
|
|
||||||
const id = e.target.parentNode.parentNode.dataset.id,
|
|
||||||
item = await apiService.getItemById(id);
|
|
||||||
|
|
||||||
this.$content.innerHTML = `Вы действительно хотите удалить товар "${item.title}"? Находящиеся в нем аккаунты будут удалены.`;
|
|
||||||
this.$button.onclick = this.deleteHandler.bind(this, item._id);
|
|
||||||
} catch (error) {
|
|
||||||
alert('Произошла ошибка, попробуйте обновить страницу.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteHandler(id) {
|
|
||||||
try {
|
|
||||||
this.$button.classList.add('hide');
|
|
||||||
this.$content.innerHTML = '<div class="progress"><div class="indeterminate"></div></div>'
|
|
||||||
await axios.post('/control-panel/items/delete', {id});
|
|
||||||
alert('Товар был успешно удален.');
|
|
||||||
document.location.reload(true);
|
|
||||||
} catch (error) {
|
|
||||||
alert('Невозможно выполнить запрос.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
import { Modal } from "../core/modal";
|
|
||||||
import { apiService } from "../services/api.service";
|
|
||||||
import { renderCategories } from "../templates/categories.template";
|
|
||||||
|
|
||||||
export class ItemEditModal extends Modal {
|
|
||||||
constructor(id, open, close, {loader}) {
|
|
||||||
super(id, open, close);
|
|
||||||
this.loader = loader
|
|
||||||
}
|
|
||||||
|
|
||||||
show(e) {
|
|
||||||
if (e.target.id === 'editItem') {
|
|
||||||
this.$el.classList.remove('hide');
|
|
||||||
document.body.style.overflowY = 'hidden';
|
|
||||||
this.openHandler(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setElems() {
|
|
||||||
this.$id = this.$el.querySelector('.id');
|
|
||||||
this.$title = this.$el.querySelector('.title');
|
|
||||||
this.$price = this.$el.querySelector('.price');
|
|
||||||
this.$description = this.$el.querySelector('.description');
|
|
||||||
this.$category = this.$el.querySelector('.category');
|
|
||||||
this.$data = this.$el.querySelector('.data');
|
|
||||||
}
|
|
||||||
|
|
||||||
async openHandler(e) {
|
|
||||||
try {
|
|
||||||
this.loader.show();
|
|
||||||
const id = e.target.parentNode.parentNode.dataset.id,
|
|
||||||
item = await apiService.getItemById(id),
|
|
||||||
data = await apiService.getDataById(id),
|
|
||||||
categories = await apiService.getCategories(),
|
|
||||||
category = await apiService.getCategory(item.category);
|
|
||||||
item.data = data.data
|
|
||||||
|
|
||||||
this.$category.innerHTML = `<option value='${category[0]._id}' selected>Оставить прежнюю</option>`;
|
|
||||||
this.$id.value = item._id;
|
|
||||||
this.$title.value = item.title;
|
|
||||||
this.$price.value = item.price;
|
|
||||||
this.$description.value = item.description;
|
|
||||||
this.$category.insertAdjacentHTML('beforeend', renderCategories(categories));
|
|
||||||
this.$data.value = item.data;
|
|
||||||
M.FormSelect.init(this.$category);
|
|
||||||
M.textareaAutoResize(this.$description);
|
|
||||||
M.textareaAutoResize(this.$data);
|
|
||||||
M.updateTextFields();
|
|
||||||
this.loader.hide();
|
|
||||||
} catch (error) {
|
|
||||||
alert('Произошла ошибка, обновите страницу')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,160 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
|
|
||||||
class ApiService {
|
|
||||||
constructor(baseUrl) {
|
|
||||||
this.url = baseUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getCategory(category) {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/category/${encodeURIComponent(category)}`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getCategoryById(id) {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/category/id/${id}`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getCategories() {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/categories`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getItems(sorting) {
|
|
||||||
try {
|
|
||||||
if (sorting) {
|
|
||||||
const request = `${this.url}/api/items?sorting=${sorting}`;
|
|
||||||
return useRequest(request);
|
|
||||||
} else {
|
|
||||||
const request = `${this.url}/api/items`;
|
|
||||||
return useRequest(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getItemsOffset(page) {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/items/page/${page}`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getItemsQuery(text) {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/items?q=${text}`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getItemById(id) {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/items/id/${id}`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDataById(id) {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/items/data/${id}`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getItemsOne(category, sorting) {
|
|
||||||
try {
|
|
||||||
if (sorting) {
|
|
||||||
const request = `${this.url}/api/items/${encodeURIComponent(category.toLowerCase())}?sorting=${sorting}`;
|
|
||||||
return useRequest(request);
|
|
||||||
} else {
|
|
||||||
const request = `${this.url}/api/items/${encodeURIComponent(category.toLowerCase())}`;
|
|
||||||
return useRequest(request);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getItemsData() {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/items/data`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getBuyers() {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/buyers`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getBuyerById(id) {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/buyers/${id}`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getBuyersOffset(page) {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/buyers/page/${page}`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getBillId(bill_id) {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/buyers/${bill_id}`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getIcons() {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/icons`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function useRequest(request) {
|
|
||||||
const response = await axios.get(request);
|
|
||||||
return await JSON.parse(response.request.response);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const apiService = new ApiService(document.defaultView.location.origin);
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
export function renderBuyers(buyers) {
|
|
||||||
if (!buyers.error) {
|
|
||||||
return buyers.map(buyer => `<tr data-id="${buyer.bill_id}"><td>${buyer.email}</td><td>${buyer.method[0].toUpperCase() + buyer.method.slice(1)}</td><td>${new Date(buyer.date).toLocaleDateString()}</td><td>${buyer.amount} ₽</td><td><button id="billDataOpen" class="btn orange">Данные аккаунтов</button></td></tr>`).join('');
|
|
||||||
} else {
|
|
||||||
return 'Покупателей в магазине пока что нету';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
export function renderCategories(categories) {
|
|
||||||
return categories.map(category => `<option value='${category._id}' data-icon="../img/service-icons/${category.img}">${category.title[0].toUpperCase() + category.title.slice(1)}</option>`).join('');
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
export function renderIcons(icons) {
|
|
||||||
return icons.map(icon => `<option value='${icon}' data-icon="../img/service-icons/${icon}">${icon}</option>`).join('');
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
export function renderItems(items, categories, info) {
|
|
||||||
if (!items.error) {
|
|
||||||
return categories.map(category => {
|
|
||||||
return items.map((item, i) => {
|
|
||||||
if (item.category.toLowerCase() === category.title.toLowerCase()) {
|
|
||||||
return `<tr data-id="${item._id}"><td><img src="../img/service-icons/${category.img}" alt="alt"></td><td>${item.title}</td><td>${item.category[0].toUpperCase() + item.category.slice(1)}</td><td>${new Date(item.date).toLocaleDateString()}</td><td>${item.count}</td><td>${item.price} ₽</td><td style="display: flex"><button id="deleteItem" class="btn red">Удалить</button><button id="editItem" class="btn orange">Изменить</button></td></tr>`;
|
|
||||||
}
|
|
||||||
}).join('');
|
|
||||||
}).join('');
|
|
||||||
} else {
|
|
||||||
return 'Данных нету.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
|
|
||||||
export class BreadcrumbComponent extends Component {
|
|
||||||
constructor(id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
if(this.$el) {
|
|
||||||
const btn = this.$el.querySelector('#breadcrumbBtn');
|
|
||||||
btn.addEventListener('click', buttonHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buttonHandler(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (e.target.dataset.category) {
|
|
||||||
localStorage.setItem('category', e.target.dataset.category.toLowerCase());
|
|
||||||
window.location.href = '/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,81 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
import { apiService } from "../services/api.service";
|
|
||||||
import { renderItems } from "../templates/items.template";
|
|
||||||
|
|
||||||
export class CategoryComponent extends Component {
|
|
||||||
constructor(id, {items}, {filter}) {
|
|
||||||
super(id);
|
|
||||||
this.items = items;
|
|
||||||
this.filter = filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
if (this.$el) {
|
|
||||||
this.$el.addEventListener('click', buttonHandler.bind(this));
|
|
||||||
|
|
||||||
if (localStorage.getItem('category')) {
|
|
||||||
localStorage.removeItem('sortByAll', 'true'); // установка значения для компонента Filter
|
|
||||||
|
|
||||||
const category = localStorage.getItem('category');
|
|
||||||
breadcrumbActiveLink.call(this, category); // окрашиваем категорию по нажатию на breadcrumb
|
|
||||||
showItems.call(this, category); // отображем айтемсы если был нажат breadcrumb
|
|
||||||
localStorage.removeItem('category'); // очищаем ключ category чтобы данные не показывались всегда
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function showItems(category) {
|
|
||||||
const targetCategory = await apiService.getCategory(category);
|
|
||||||
const fData = await apiService.getItemsOne(category),
|
|
||||||
html = renderItems(targetCategory, fData);
|
|
||||||
|
|
||||||
this.items.onHide();
|
|
||||||
this.items.insertItems(html);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function buttonHandler(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (e.target.dataset.category && e.target.dataset.category != 'all') { // Выполняем логику если нажали на кнопки кроме "Все товары"
|
|
||||||
localStorage.removeItem('sortByAll'); // установка значения для компонента Filter
|
|
||||||
|
|
||||||
this.filter.clearChecked();
|
|
||||||
this.items.onShow();
|
|
||||||
btnActiveLink(e);
|
|
||||||
|
|
||||||
const category = await apiService.getCategory(e.target.dataset.category),
|
|
||||||
fData = await apiService.getItemsOne(e.target.dataset.category),
|
|
||||||
html = renderItems(category, fData);
|
|
||||||
|
|
||||||
this.items.onHide();
|
|
||||||
this.items.insertItems(html);
|
|
||||||
} else if (e.target.dataset.category === 'all') { // Выполняем логику если нажали на кнопку "Все товары"
|
|
||||||
localStorage.setItem('sortByAll', 'true'); // установка значения для компонента Filter
|
|
||||||
|
|
||||||
this.filter.clearChecked();
|
|
||||||
this.items.onShow();
|
|
||||||
btnActiveLink(e);
|
|
||||||
|
|
||||||
const fData = await apiService.getItems();
|
|
||||||
const categories = await apiService.getCategories();
|
|
||||||
const html = renderItems(categories, fData);
|
|
||||||
|
|
||||||
this.items.onHide();
|
|
||||||
this.items.insertItems(html);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Окраска li по нажатию
|
|
||||||
function btnActiveLink(e) {
|
|
||||||
Array.from(e.target.parentNode.parentNode.querySelectorAll('#catLi')).forEach(li => li.classList.remove('active'));
|
|
||||||
e.target.classList.add('active');
|
|
||||||
localStorage.setItem('currentCategory', e.target.dataset.category);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Окраска li по нажатию на breadcrumb
|
|
||||||
function breadcrumbActiveLink(category) {
|
|
||||||
const links = this.$el.querySelectorAll('#catLi');
|
|
||||||
Array.from(links).forEach(li => li.classList.remove('active'));
|
|
||||||
Array.from(links).forEach(li => li.dataset.category === category ? li.classList.add('active') : false);
|
|
||||||
Array.from(links).forEach(li => li.dataset.category === category ? localStorage.setItem('currentCategory', li.dataset.category) : false);
|
|
||||||
}
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
import { apiService } from "../services/api.service";
|
|
||||||
import { renderItems } from "../templates/items.template";
|
|
||||||
|
|
||||||
export class FilterComponent extends Component {
|
|
||||||
constructor(id, {items}) {
|
|
||||||
super(id);
|
|
||||||
this.items = items;
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
if (this.$el) {
|
|
||||||
this.$el.addEventListener('click', sortByAllHandler.bind(this));
|
|
||||||
this.$el.addEventListener('click', sortByOneHandler.bind(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearChecked() {
|
|
||||||
if (this.$el) {
|
|
||||||
const options = this.$el.querySelectorAll('.filter-options form input');
|
|
||||||
Array.from(options).forEach(option => option.checked = false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sortByAllHandler(e) {
|
|
||||||
if (localStorage.getItem('sortByAll')) {
|
|
||||||
if (e.target.id) {
|
|
||||||
const sorting = e.target.id;
|
|
||||||
|
|
||||||
this.items.onShow();
|
|
||||||
const fData = await apiService.getItems(sorting);
|
|
||||||
const categories = await apiService.getCategories();
|
|
||||||
const html = renderItems(categories, fData);
|
|
||||||
|
|
||||||
this.items.onHide();
|
|
||||||
this.items.insertItems(html);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sortByOneHandler(e) {
|
|
||||||
if (!localStorage.getItem('sortByAll')) {
|
|
||||||
if (e.target.id) {
|
|
||||||
const sorting = e.target.id;
|
|
||||||
this.items.onShow();
|
|
||||||
|
|
||||||
const category = await apiService.getCategory(localStorage.getItem('currentCategory')),
|
|
||||||
fData = await apiService.getItemsOne(localStorage.getItem('currentCategory'), sorting),
|
|
||||||
html = renderItems(category, fData);
|
|
||||||
|
|
||||||
this.items.onHide();
|
|
||||||
this.items.insertItems(html);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
import { insertCount } from "../other/counter";
|
|
||||||
|
|
||||||
export class HeaderComponent extends Component {
|
|
||||||
constructor(id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
if (this.$el) {
|
|
||||||
const currentPage = this.$el.querySelector('#desktop-nav');
|
|
||||||
const pages = Array.from(this.$el.querySelectorAll('#page'));
|
|
||||||
pages.forEach(li => li.dataset.page === currentPage.dataset.current ? li.classList.add('active') : false);
|
|
||||||
// ф-я счетика товаров
|
|
||||||
localStorage.getItem('cart') ? insertCount('counter', JSON.parse(localStorage.getItem('cart'))) : false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
|
|
||||||
export class InformationComponent extends Component {
|
|
||||||
constructor(id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
if (this.$el) {
|
|
||||||
if (!localStorage.getItem('visited')) {
|
|
||||||
this.show();
|
|
||||||
}
|
|
||||||
const btn = this.$el.querySelector('#information-close');
|
|
||||||
btn.addEventListener('click', buttonHandler.bind(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buttonHandler() {
|
|
||||||
localStorage.setItem('visited', JSON.stringify(true));
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
import { Component } from '../core/component'
|
|
||||||
import { apiService } from '../services/api.service'
|
|
||||||
import { insertCount } from '../other/counter'
|
|
||||||
import { renderItems } from '../templates/items.template'
|
|
||||||
|
|
||||||
export class ItemsComponent extends Component {
|
|
||||||
constructor(id, { loader }, { notification }) {
|
|
||||||
super(id)
|
|
||||||
this.loader = loader
|
|
||||||
this.notification = notification
|
|
||||||
this.$el ? (this.roster = this.$el.querySelector('#roster')) : false
|
|
||||||
if (localStorage.getItem('cart')) {
|
|
||||||
this.cart = JSON.parse(localStorage.getItem('cart'))
|
|
||||||
} else {
|
|
||||||
this.cart = []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
if (this.$el) {
|
|
||||||
localStorage.setItem('sortByAll', 'true') // установка значения для компонента Filter
|
|
||||||
this.$el.addEventListener('click', addItem.bind(this))
|
|
||||||
if (!localStorage.getItem('category')) {
|
|
||||||
// Получаем все товары и выводим их если нету ключа category в ls
|
|
||||||
const fData = await apiService.getItems()
|
|
||||||
const categories = await apiService.getCategories()
|
|
||||||
const html = renderItems(categories, fData)
|
|
||||||
this.insertItems(html)
|
|
||||||
this.btnColor()
|
|
||||||
this.loader.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onHide() {
|
|
||||||
this.$el.querySelector('#roster').innerHTML = ''
|
|
||||||
this.loader.hide()
|
|
||||||
}
|
|
||||||
|
|
||||||
onShow() {
|
|
||||||
this.$el.querySelector('#roster').innerHTML = ''
|
|
||||||
this.loader.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
btnColor() {
|
|
||||||
if (localStorage.getItem('cart')) {
|
|
||||||
// Окраска кнопок айтемов которые находятся в корзине
|
|
||||||
const obj = JSON.parse(localStorage.getItem('cart'))
|
|
||||||
|
|
||||||
obj.forEach((item) => {
|
|
||||||
if (
|
|
||||||
this.$el.querySelector(
|
|
||||||
`#rosterButtons[data-id="${item.id}"] #addItem`
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
this.$el
|
|
||||||
.querySelector(`#rosterButtons[data-id="${item.id}"] #addItem`)
|
|
||||||
.classList.add('orange')
|
|
||||||
this.$el.querySelector(
|
|
||||||
`#rosterButtons[data-id="${item.id}"] #addItem i`
|
|
||||||
).innerHTML = 'close'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
insertItems(html) {
|
|
||||||
this.roster.insertAdjacentHTML('afterbegin', html)
|
|
||||||
this.btnColor()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addItem(e) {
|
|
||||||
if (e.target.localName === 'button' && e.target.id === 'addItem') {
|
|
||||||
const target = e.target
|
|
||||||
const item = {
|
|
||||||
id: e.target.parentNode.dataset.id,
|
|
||||||
title: e.target.parentNode.dataset.title,
|
|
||||||
count: e.target.parentNode.dataset.count,
|
|
||||||
price: e.target.parentNode.dataset.price,
|
|
||||||
}
|
|
||||||
renderObj(item, this, target)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderObj(obj, context, target) {
|
|
||||||
if (context.cart.length > 0) {
|
|
||||||
let insterted = false
|
|
||||||
let index
|
|
||||||
// Проверка на существующий айтем в корзине
|
|
||||||
context.cart.forEach((item, i) => {
|
|
||||||
if (item.id === obj.id) {
|
|
||||||
insterted = true
|
|
||||||
index = i
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (insterted === false) {
|
|
||||||
context.cart.push(obj)
|
|
||||||
localStorage.setItem('cart', JSON.stringify(context.cart))
|
|
||||||
target.classList.add('orange')
|
|
||||||
target.querySelector('i').innerHTML = 'close'
|
|
||||||
insertCount('counter', context.cart)
|
|
||||||
window.M.toast({ html: 'Товар был успешно добавлен в корзину.' })
|
|
||||||
} else {
|
|
||||||
context.cart.splice(index, 1)
|
|
||||||
localStorage.setItem('cart', JSON.stringify(context.cart))
|
|
||||||
target.classList.remove('orange')
|
|
||||||
target.querySelector('i').innerHTML = 'shopping_cart'
|
|
||||||
insertCount('counter', context.cart)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
context.cart.push(obj)
|
|
||||||
localStorage.setItem('cart', JSON.stringify(context.cart))
|
|
||||||
target.classList.add('orange')
|
|
||||||
target.querySelector('i').innerHTML = 'close'
|
|
||||||
insertCount('counter', context.cart)
|
|
||||||
window.M.toast({ html: 'Товар был успешно добавлен в корзину.' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
|
|
||||||
export class LoaderComponent extends Component {
|
|
||||||
constructor(id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
|
|
||||||
export class NotificationComponent extends Component {
|
|
||||||
constructor(id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
notificate(text, color, delay) {
|
|
||||||
const card = document.createElement('div');
|
|
||||||
card.classList.add('card-panel');
|
|
||||||
card.style.backgroundColor = color;
|
|
||||||
card.innerHTML = `<span class="white-text">${text}</span>`;
|
|
||||||
this.$el.innerHTML += card.outerHTML;
|
|
||||||
this.removeNotificate(delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeNotificate(delay) {
|
|
||||||
// Полифилл для IE ^9
|
|
||||||
(function() {
|
|
||||||
var arr = [window.Element, window.CharacterData, window.DocumentType];
|
|
||||||
var args = [];
|
|
||||||
|
|
||||||
arr.forEach(function (item) {
|
|
||||||
if (item) {
|
|
||||||
args.push(item.prototype);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
(function (arr) {
|
|
||||||
arr.forEach(function (item) {
|
|
||||||
if (item.hasOwnProperty('remove')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Object.defineProperty(item, 'remove', {
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
writable: true,
|
|
||||||
value: function remove() {
|
|
||||||
this.parentNode.removeChild(this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})(args);
|
|
||||||
})();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$el.querySelector('.card-panel').remove();
|
|
||||||
}, delay || 2000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
import { Component } from "../core/component";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
export class OrdersComponent extends Component {
|
|
||||||
constructor(id, {notification}, {loader}) {
|
|
||||||
super(id);
|
|
||||||
this.notification = notification;
|
|
||||||
this.loader = loader;
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
if (this.$el) {
|
|
||||||
let valid = true;
|
|
||||||
this.$el.querySelector('form').onsubmit = async (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (valid) {
|
|
||||||
valid = false;
|
|
||||||
this.loader.show();
|
|
||||||
const email = e.target.elements.email.value,
|
|
||||||
request = await axios.post('/my-orders', {email});
|
|
||||||
|
|
||||||
switch (request.data.status) {
|
|
||||||
case 'OK':
|
|
||||||
window.M.toast({html: `Данные были успешно высланы на почту "${email}".`})
|
|
||||||
break;
|
|
||||||
case 'NF':
|
|
||||||
window.M.toast({html: `Покупатель с почтой "${email}" не был найден.`})
|
|
||||||
break;
|
|
||||||
case 'ERR':
|
|
||||||
window.M.toast({html: `Произошла ошибка, попробуйте повторить запрос позже.`})
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.loader.hide();
|
|
||||||
setTimeout(() => valid = true, 10000);
|
|
||||||
} else {
|
|
||||||
window.M.toast({html: `Подождите еще нескольно секунд прежде чем сделать запрос...`})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
const M = require('../frameworks/materialize.min');
|
|
||||||
export class MaterializeConfig {
|
|
||||||
constructor() {
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
M.Sidenav.init(document.querySelectorAll('.sidenav'))
|
|
||||||
M.FormSelect.init(document.querySelectorAll('select'))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
export class Component {
|
|
||||||
constructor(id) {
|
|
||||||
this.$el = document.getElementById(id);
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
onShow() {}
|
|
||||||
|
|
||||||
onHide() {}
|
|
||||||
|
|
||||||
hide() {
|
|
||||||
this.$el ? this.$el.classList.add('hide') : false
|
|
||||||
}
|
|
||||||
|
|
||||||
show() {
|
|
||||||
this.$el ? this.$el.classList.remove('hide') : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
export class Modal {
|
|
||||||
constructor(id, open, close) {
|
|
||||||
this.$el = document.getElementById(id);
|
|
||||||
this.open = document.getElementById(open);
|
|
||||||
this.close = document.getElementById(close);
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
if (this.open != null) {
|
|
||||||
this.open.addEventListener('click', openHandler.bind(this));
|
|
||||||
this.close.addEventListener('click', closeHandler.bind(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
show() {
|
|
||||||
this.$el.classList.remove('hide');
|
|
||||||
document.body.style.overflowY = 'hidden';
|
|
||||||
}
|
|
||||||
|
|
||||||
hide() {
|
|
||||||
this.$el.classList.add('hide');
|
|
||||||
document.body.style.overflowY = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function openHandler() {
|
|
||||||
this.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeHandler() {
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
6
src/main/frameworks/materialize.min.js
vendored
6
src/main/frameworks/materialize.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,30 +0,0 @@
|
|||||||
import { HeaderComponent } from "./components/header.component.js";
|
|
||||||
import { InformationComponent } from "./components/information.component.js";
|
|
||||||
import { LoaderComponent } from "./components/loader.component.js";
|
|
||||||
import { ItemsComponent } from "./components/items.component.js";
|
|
||||||
import { CategoryComponent } from "./components/category.component.js";
|
|
||||||
import { BreadcrumbComponent } from "./components/breadcrumb.component.js";
|
|
||||||
import { FilterComponent } from "./components/filter.component.js";
|
|
||||||
import { CartModal } from "./modals/cart.modal.js";
|
|
||||||
import { PayModal } from "./modals/pay.modal.js";
|
|
||||||
import { NotificationComponent } from "./components/notification.component.js";
|
|
||||||
import { MaterializeConfig } from "./config/materialize.config.js";
|
|
||||||
import { OrdersComponent } from "./components/orders.component.js";
|
|
||||||
|
|
||||||
// Components
|
|
||||||
const header = new HeaderComponent('header');
|
|
||||||
const loader = new LoaderComponent('loader');
|
|
||||||
const information = new InformationComponent('information');
|
|
||||||
const notification = new NotificationComponent('notification');
|
|
||||||
const items = new ItemsComponent('items', {loader}, {notification});
|
|
||||||
const filter = new FilterComponent('filter', {items});
|
|
||||||
const category = new CategoryComponent('category', {items}, {filter});
|
|
||||||
const breadcumb = new BreadcrumbComponent('breadcrumb');
|
|
||||||
const orders = new OrdersComponent('orders', {notification}, {loader});
|
|
||||||
|
|
||||||
// Modals
|
|
||||||
const cart = new CartModal('cart', 'cartOpen', 'cartClose', {items});
|
|
||||||
const pay = new PayModal('pay', 'pay-js', 'payClose');
|
|
||||||
|
|
||||||
const materialize = new MaterializeConfig();
|
|
||||||
console.log('Main Page');
|
|
||||||
@ -1,96 +0,0 @@
|
|||||||
import { Modal } from "../core/modal";
|
|
||||||
import { insertCount } from "../other/counter";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
export class CartModal extends Modal {
|
|
||||||
constructor(id, open, close, {items}) {
|
|
||||||
super(id, open, close);
|
|
||||||
this.items = items;
|
|
||||||
if (this.$el) {
|
|
||||||
this.open.addEventListener('click', this.openHandler.bind(this));
|
|
||||||
this.close.addEventListener('click', this.closeHandler.bind(this));
|
|
||||||
this.list = this.$el.querySelector('#modalList');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closeHandler() {
|
|
||||||
this.removeItem = null;
|
|
||||||
this.makePay = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
openHandler() {
|
|
||||||
this.removeItem != null ? this.list.addEventListener('click', this.removeItem.bind(this)) : false
|
|
||||||
this.makePay != null ? this.$el.querySelector('form').addEventListener('submit', this.makePay.bind(this)) : false;
|
|
||||||
this.list.innerHTML = '';
|
|
||||||
this.insertItems(this.list);
|
|
||||||
}
|
|
||||||
|
|
||||||
insertItems(list) {
|
|
||||||
if (localStorage.getItem('cart')) {
|
|
||||||
const items = JSON.parse(localStorage.getItem('cart'));
|
|
||||||
|
|
||||||
if (items.length == 0) {
|
|
||||||
list.innerHTML = '<p style="text-align:center;padding:15px;">Вы не добавили ни один товар</p>';
|
|
||||||
this.$el.querySelector('form').classList.add('hide');
|
|
||||||
} else {
|
|
||||||
this.$el.querySelector('form').classList.remove('hide');
|
|
||||||
}
|
|
||||||
|
|
||||||
items.forEach(item => this.list.innerHTML += `<div data-id="${item.id}" data-title="${item.title}" data-price="${item.price}" data-count="1" class="modal-content__item"><div class="modal-content__title"><a href="/product/${item.id}"><b>${item.title}</b></a><br><span class="in-stock">В наличии: ${item.count} (шт)</span></div><div class="modal-content__right"><span id="price">${parseFloat(item.price).toFixed(2)}₽</span></div><div class="modal-content__remove"><span><i data-id="${item.id}" class="material-icons" id="removeItem">close</i></span></div></div>`);
|
|
||||||
insertCount('modalCounter', this.items.cart);
|
|
||||||
insertCount('counter', this.items.cart);
|
|
||||||
this.insertTotalPrice('totalPrice', this.items.cart);
|
|
||||||
} else {
|
|
||||||
list.innerHTML = '<p style="text-align:center;padding:15px;">Вы не добавили ни один товар</p>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeItem(e) {
|
|
||||||
if (e.target.id === 'removeItem') {
|
|
||||||
const id = e.target.dataset.id;
|
|
||||||
let index;
|
|
||||||
|
|
||||||
this.items.cart.forEach((item, i) => item.id === id ? index = i : false);
|
|
||||||
this.items.cart.splice(index, 1);
|
|
||||||
localStorage.setItem('cart', JSON.stringify(this.items.cart));
|
|
||||||
|
|
||||||
this.list.innerHTML = '';
|
|
||||||
this.insertItems(this.list);
|
|
||||||
this.defaultBtnColor(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
insertTotalPrice(id, array) {
|
|
||||||
let totalPrice = 0;
|
|
||||||
array.forEach(item => totalPrice += parseFloat(item.price));
|
|
||||||
this.$el.querySelector(`#${id}`).innerHTML = `${totalPrice.toFixed(2)}₽`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async makePay(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const payInfo = {items: this.items.cart.map(item => {
|
|
||||||
return {
|
|
||||||
id: item.id,
|
|
||||||
title: item.title,
|
|
||||||
price: parseFloat(item.price)
|
|
||||||
}
|
|
||||||
})};
|
|
||||||
let totalPrice = 0;
|
|
||||||
payInfo.items.forEach(item => totalPrice += parseFloat(item.price));
|
|
||||||
payInfo.totalPrice = Math.round(totalPrice * 100) / 100;
|
|
||||||
payInfo.payMethod = this.$el.querySelector('.payment-field input').value;
|
|
||||||
payInfo.email = this.$el.querySelector('.email-field input').value;
|
|
||||||
|
|
||||||
const response = await axios.put('/payment', payInfo);
|
|
||||||
response.data.payUrl ? document.location.href = response.data.payUrl : alert(response.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultBtnColor(id) {
|
|
||||||
if (this.items.roster) {
|
|
||||||
if (this.items.roster.querySelector(`#rosterButtons[data-id="${id}"] #addItem`)) {
|
|
||||||
this.items.roster.querySelector(`#rosterButtons[data-id="${id}"] #addItem`).classList.remove('orange');
|
|
||||||
this.items.roster.querySelector(`#rosterButtons[data-id="${id}"] #addItem i`).innerHTML = 'shopping_cart';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
import { Modal } from "../core/modal";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
export class PayModal extends Modal {
|
|
||||||
constructor (id, open, close) {
|
|
||||||
super(id, open, close);
|
|
||||||
if (this.open != null) {
|
|
||||||
this.open.addEventListener('click', this.openHandler.bind(this));
|
|
||||||
this.close.addEventListener('click', this.closeHandler.bind(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closeHandler() {
|
|
||||||
this.$el.querySelector('form').onsubmit = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
openHandler(e) {
|
|
||||||
if (e.target.id === 'payOpen') {
|
|
||||||
const targetInfo = e.target.parentNode.dataset;
|
|
||||||
this.$el.querySelector('.title-field input').value = targetInfo.title;
|
|
||||||
|
|
||||||
this.show();
|
|
||||||
this.$el.querySelector('form').onsubmit = (e) => {
|
|
||||||
this.makePay(e, targetInfo);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async makePay(e, info) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
const payInfo = {
|
|
||||||
items: [{
|
|
||||||
id: info.id,
|
|
||||||
title: info.title
|
|
||||||
}],
|
|
||||||
totalPrice: parseFloat(info.price)
|
|
||||||
}
|
|
||||||
payInfo.payMethod = this.$el.querySelector('.payment-field input').value;
|
|
||||||
payInfo.email = this.$el.querySelector('.email-field input').value;
|
|
||||||
|
|
||||||
const response = await axios.put('/payment', payInfo);
|
|
||||||
response.data.payUrl ? document.location.href = response.data.payUrl : alert(response.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
export function insertCount(id, array) {
|
|
||||||
const counter = document.getElementById(id);
|
|
||||||
counter.innerHTML = array.length;
|
|
||||||
}
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
class ApiService {
|
|
||||||
constructor(baseUrl) {
|
|
||||||
this.url = baseUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getCategory(category) {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/category/${encodeURIComponent(category)}`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getCategories() {
|
|
||||||
try {
|
|
||||||
const request = `${this.url}/api/categories`;
|
|
||||||
return useRequest(request);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getItems(sorting) {
|
|
||||||
try {
|
|
||||||
if (sorting) {
|
|
||||||
const request = `${this.url}/api/items?sorting=${sorting}`;
|
|
||||||
return useRequest(request);
|
|
||||||
} else {
|
|
||||||
const request = `${this.url}/api/items`;
|
|
||||||
return useRequest(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getItemsOne(category, sorting) {
|
|
||||||
try {
|
|
||||||
if (sorting) {
|
|
||||||
const request = `${this.url}/api/items/${encodeURIComponent(category.toLowerCase())}?sorting=${sorting}`;
|
|
||||||
return useRequest(request);
|
|
||||||
} else {
|
|
||||||
const request = `${this.url}/api/items/${encodeURIComponent(category.toLowerCase())}`;
|
|
||||||
return useRequest(request);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function useRequest(request) {
|
|
||||||
const response = await axios.get(request);
|
|
||||||
return await JSON.parse(response.request.response);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const apiService = new ApiService(document.defaultView.location.origin);
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
export function renderItems(categories, items) {
|
|
||||||
let html = '';
|
|
||||||
try {
|
|
||||||
categories.forEach(category => {
|
|
||||||
html += `<div class="items-category">
|
|
||||||
<h2 class="small-title">${category.title}</h2>
|
|
||||||
</div>`;
|
|
||||||
category.itemsInCategory = 0; // счетчит для отслеживания категорий где нету айтемов
|
|
||||||
try {
|
|
||||||
items.forEach(item => {
|
|
||||||
if (item.category.toLowerCase() === category.title.toLowerCase()) {
|
|
||||||
category.itemsInCategory += 1;
|
|
||||||
html += `
|
|
||||||
<div class="items-list">
|
|
||||||
<div class="items-list__name">
|
|
||||||
<div class="list-icon">
|
|
||||||
<img src="./img/service-icons/${category.img}" alt="#">
|
|
||||||
</div>
|
|
||||||
<div class="list-title">
|
|
||||||
<a href="/product/${item._id}">${item.title}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="items-list__info">
|
|
||||||
<div class="list-date">
|
|
||||||
<small>${diffDates(new Date(), new Date(item.date))}</small>
|
|
||||||
</div>
|
|
||||||
<div class="list-count">
|
|
||||||
<span>${item.count} шт.</span>
|
|
||||||
</div>
|
|
||||||
<div class="list-one">
|
|
||||||
<span>Цена за 1 шт.</span>
|
|
||||||
</div>
|
|
||||||
<div class="list-price">
|
|
||||||
<span>${item.price.toFixed(2)}₽</span>
|
|
||||||
</div>
|
|
||||||
<div id="rosterButtons" data-id="${item._id}" data-title="${item.title}" data-count="${item.count}" data-price="${item.price} class="list-buttons">
|
|
||||||
<button id="payOpen" class="btn-small waves-effect waves-light">Купить</button>
|
|
||||||
<button id="addItem" class="btn-small waves-effect waves-light"><i
|
|
||||||
class="material-icons">shopping_cart</i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
category.itemsInCategory === 0
|
|
||||||
? html += '<div class="items-list"><span>В этой категории товаров пока что нету.</span></div>'
|
|
||||||
: false;
|
|
||||||
} catch {
|
|
||||||
html += '<div class="items-list"><span>В этой категории товаров пока что нету.</span></div>';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
html += '<div class="items-list"><span>Добавьте хотя бы одну категорию.</span></div>';
|
|
||||||
}
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
function diffDates(firstDate, secondDate) {
|
|
||||||
let diff = Math.floor((firstDate - secondDate) / (60 * 60 * 24 * 1000));
|
|
||||||
if (diff <= 0) {
|
|
||||||
return 'добавлено сегодня';
|
|
||||||
}
|
|
||||||
return diff + ' дней назад';
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
entry: {
|
|
||||||
admin: ['@babel/polyfill', './src/admin/index.js'],
|
|
||||||
main: ['@babel/polyfill', './src/main/index.js']
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
filename: '[name].js',
|
|
||||||
path: path.join(__dirname, '/dist/public/js/')
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
extensions: ['.js']
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
devServer: {
|
|
||||||
contentBase: './dist/public/js',
|
|
||||||
proxy: {
|
|
||||||
"/": 'http://localhost:3000'
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user