deleted src

This commit is contained in:
Sergey Yarkov 2020-01-18 18:53:28 +03:00
parent 01b66f81f0
commit b7b67214ab
41 changed files with 0 additions and 1512 deletions

3
.gitignore vendored
View File

@ -1,3 +0,0 @@
node_modules
package-lock.json
.env

View File

@ -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('Невозможно выполнить запрос.');
}
}
}

View File

@ -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('Выберите категорию для удаления.');
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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('Выберите иконку для удаления.');
}
}

View File

@ -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);
}
}

View File

@ -1,7 +0,0 @@
import { Component } from "../core/component";
export class LoaderComponent extends Component {
constructor(id) {
super(id);
}
}

View File

@ -1,92 +0,0 @@
import { Component } from "../core/component";
export class PaginationComponent extends Component {
constructor(id, {loader}) {
super(id);
this.loader = loader;
}
// вставляем кнопки со страницами и назначаем ивент
initialize(pages, insertTo, request) {
this.insertButtons(pages);
this.$pages = Array.from(this.$el.querySelectorAll('li'));
this.$el.onclick = (e) => this.buttonHandler(e, insertTo, request);
this.moveTo(1, insertTo, request); // показываем первую страницу
this.$el.querySelector('li').nextElementSibling.classList.add('active');
}
removeActive() {
this.$pages.forEach(li => li.classList.remove('active'));
}
setActive(li, value) {
li.forEach(li => {const page = parseInt(li.querySelector('a').dataset.move);page === value ? li.classList.add('active') : false;});
}
buttonHandler(e, insertTo, request) {
e.preventDefault();
if (e.target.dataset.move) {
const page = parseInt(e.target.dataset.move),
target = e.target.parentNode;
this.removeActive();
target.classList.add('active');
this.moveTo(page, insertTo, request);
} else if (e.target.tagName.toLowerCase() === 'i') {
const moveType = e.target.parentNode.dataset.type,
page = parseInt(this.$el.dataset.page);
moveType === 'prev' ? this.movePrev(page, this.$pages, insertTo, request) : this.moveNext(page, this.$pages, insertTo, request);
}
}
movePrev(page, li, insertTo, request) {
if (page === 1) return 0; else {
--page;
this.removeActive();
this.setActive(li, page);
this.moveTo(page, insertTo, request);
}
}
// выполняем запрос на сервер и выводим на данные
async moveTo(page, insertTo, request) {
this.loader.show();
insertTo.innerHTML = '';
const response = await request(page);
if (response != '') {
this.$el.setAttribute('data-page', page);
insertTo.insertAdjacentHTML('afterbegin', response);
} else {
insertTo.innerHTML = 'Данных нету.';
}
this.loader.hide();
}
moveNext(page, li, insertTo, request) {
const length = this.$pages.length - 2;
if (page === length) return 0; else {
++page;
this.removeActive();
this.setActive(li, page);
this.moveTo(page, insertTo, request);
}
}
insertButtons(pages) {
const count = Math.ceil(pages / 5);
let html = '';
// назад
html += '<li><a class="disabled" href="#" data-type="prev"><i class="material-icons">chevron_left</i></a></li>';
// все страницы
for (let i = 1; i <= count; i++) {
html += `<li><a href="#" data-move="${i}">${i}</a></li>`;
}
// вперед
html += '<li><a class="disabled" href="#" data-type="next"><i class="material-icons">chevron_right</i></a></li>';
this.$el.insertAdjacentHTML('afterbegin', html);
}
}

View File

@ -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('Произошла ошибка.');
}
}
}

View File

@ -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();
}
}
});
}
});
}
}

View File

@ -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();
}
}

View File

@ -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() {}
}

File diff suppressed because one or more lines are too long

View File

@ -1,32 +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";
// 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');
const itemDelete = new ItemDeleteModal('deleteItemModal', 'edit', 'deleteItemClose');
const categoryEdit = new CategoryEditModal('editCategoryModal', 'editCategory_select', 'editCategoryClose');
const materialize = new MaterializeConfig();
console.log('Control-Panel v1.0.0');

View File

@ -1,53 +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) {
super(id, open, close);
}
// перепишем метод для перевода ивента 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) {
const data = JSON.parse(e.target.value),
icons = await apiService.getIcons();
this.$img.innerHTML = `<option value='${data.img}' selected>Оставить прежнюю</option>`;
this.$id.value = data.id;
this.$originalTitle.value = data.title[0].toUpperCase() + data.title.slice(1);
this.$title.value = data.title[0].toUpperCase() + data.title.slice(1);
this.$type.value = data.type;
this.$format.value = data.format;
this.$img.insertAdjacentHTML('beforeend', renderIcons(icons));
M.FormSelect.init(this.$img);
M.textareaAutoResize(this.$type);
M.updateTextFields();
}
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 не существует.');
}
}

View File

@ -1,39 +0,0 @@
import { Modal } from "../core/modal";
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');
}
openHandler(e) {
const data = JSON.parse(e.target.parentNode.parentNode.dataset.info);
this.$content.innerHTML = `Вы действительно хотите удалить товар "${data.title}"? Находящиеся в нем аккаунты будут удалены.`;
this.$button.onclick = this.deleteHandler.bind(this, data.id);
}
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('Невозможно выполнить запрос.');
}
}
}

View File

@ -1,43 +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) {
super(id, open, close);
}
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) {
const data = JSON.parse(e.target.parentNode.parentNode.dataset.info),
categories = await apiService.getCategories();
this.$category.innerHTML = `<option value='{"title": "${data.category}"}' selected>Оставить прежнюю</option>`;
this.$id.value = data.id;
this.$title.value = data.title;
this.$price.value = data.price;
this.$description.value = data.description;
this.$category.insertAdjacentHTML('beforeend', renderCategories(categories));
this.$data.value = data.data;
M.FormSelect.init(this.$category);
M.textareaAutoResize(this.$description);
M.textareaAutoResize(this.$data);
M.updateTextFields();
}
}

View File

@ -1,124 +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 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 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 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('http://localhost:3000');

View File

@ -1,7 +0,0 @@
export function renderBuyers(buyers) {
let html = '';
buyers.forEach(buyer => {
html += `<tr><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></tr>`;
});
return html;
}

View File

@ -1,7 +0,0 @@
export function renderCategories(categories) {
let html = '';
categories.forEach(category => {
html += `<option value='{"id": "${category._id}", "title": "${category.title}", "img": "${category.img}", "type": "${category.type}", "format": "${category.format}"}' data-icon="../img/service-icons/${category.img}">${category.title[0].toUpperCase() + category.title.slice(1)}</option>`;
});
return html;
}

View File

@ -1,7 +0,0 @@
export function renderIcons(icons) {
let html = '';
icons.forEach(icon => {
html += `<option value='${icon}' data-icon="../img/service-icons/${icon}">${icon}</option>`;
});
return html;
}

View File

@ -1,13 +0,0 @@
export function renderItems(items, categories, info) {
let html = '';
if (!items.error) {
categories.forEach(category => {
items.forEach((item, i) => {
if (item.category.toLowerCase() === category.title.toLowerCase()) html += `<tr data-info='{"id": "${item._id}", "title": "${item.title}", "category": "${item.category}", "price": "${item.price}", "description": "${item.description}", "data": "${info[i].data}"}'><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><button id="deleteItem" class="btn red">Удалить</button><button id="editItem" class="btn orange">Изменить</button></td></tr>`
});
});
} else html += 'Данных нету.';
return html;
}

View File

@ -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 = '/';
}
}

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

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

View File

@ -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();
}

View File

@ -1,120 +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);
} else if (e.target.localName === 'i') {
const target = e.target.parentNode;
const item = {
id: e.target.parentNode.parentNode.dataset.id,
title: e.target.parentNode.parentNode.dataset.title,
count: e.target.parentNode.parentNode.dataset.count,
price: e.target.parentNode.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);
context.notification.notificate('Товар был успешно добавлен в корзину.', '#26A69A');
} 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);
context.notification.notificate('Товар был успешно добавлен в корзину.', '#26A69A');
}
};

View File

@ -1,7 +0,0 @@
import { Component } from "../core/component";
export class LoaderComponent extends Component {
constructor(id) {
super(id);
}
}

View File

@ -1,50 +0,0 @@
import { Component } from "../core/component";
export class NotificationComponent extends Component {
constructor(id) {
super(id);
}
notificate(text, color) {
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();
}
removeNotificate() {
// Полифилл для 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();
}, 2000)
}
}

View File

@ -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'))
});
}
}

View File

@ -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
}
}

View File

@ -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();
}

File diff suppressed because one or more lines are too long

View File

@ -1,28 +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";
// 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');
// 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');

View File

@ -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';
}
}
}
}

View File

@ -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);
}
}

View File

@ -1,4 +0,0 @@
export function insertCount(id, array) {
const counter = document.getElementById(id);
counter.innerHTML = array.length;
}

View File

@ -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('http://localhost:3000');

View File

@ -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 + ' дней назад';
}