diff --git a/config/routes/routes.js b/config/routes/routes.js index e924846..e127c75 100644 --- a/config/routes/routes.js +++ b/config/routes/routes.js @@ -97,6 +97,9 @@ function getItemsRoute(req, res, dbo) { ? res.send(result) : res.send({error: 'На сайте нету категорий'}); }); + } else if (req.query.icons === 'all') { + const icons = fs.readdirSync('dist/public/img/service-icons'); + res.send(icons); } else if (req.query.category) { dbo.collection('categories').find({title: req.query.category}).toArray((err, result) => { res.send(result); @@ -228,10 +231,9 @@ function controlPanelDatabaseRoute(req, res) { function controlPanelAddCategory(req, res, db) { try { - const filedata = req.file; const data = { title: req.body.title.toLowerCase().trim(), - img: filedata.filename, + img: req.body.img, type: req.body.type, format: req.body.format } @@ -246,14 +248,46 @@ function controlPanelDellCategory(req, res, db) { try { const data = JSON.parse(req.body.category); db.collection('categories').deleteOne({"_id": ObjectID(data.id)}); - - // Удаляем иконку - const path = `./dist/public/img/service-icons/${data.img}` - try { - fs.unlinkSync(path); - } catch (error) { - console.log(error); + res.redirect('/control-panel/categories'); + } catch (error) { + console.log(error); + res.render('main/404'); + } +} + +function controlPanelUpdCategory(req, res, db) { + try { + const data = { + id: req.body.id, + title: req.body.title.toLowerCase().trim(), + originalTitle: req.body.originalTitle.toLowerCase().trim(), + img: req.body.img, + type: req.body.type, + format: req.body.format } + db.collection('categories').updateOne({"_id": ObjectID(data.id)}, {$set: {title: data.title, img: data.img, type: data.type, format: data.format}}); + db.collection('items').updateMany({"category": data.originalTitle}, {$set: {category: data.title}}); + res.redirect('/control-panel/categories'); + + } catch (error) { + console.log(error); + res.render('main/404'); + } +} + +function controlPanelAddIcon(req, res, db) { + try { + res.redirect('/control-panel/categories'); + } catch (error) { + console.log(error); + res.render('main/404'); + } +} + +function controlPanelDellIcon(req, res, db) { + const path = `./dist/public/img/service-icons/${req.body.img}` + try { + fs.unlinkSync(path); res.redirect('/control-panel/categories'); } catch (error) { console.log(error); @@ -278,4 +312,7 @@ module.exports = function (server, db) { server.post('/control-panel/categories/create', checkAuthenticated, (req, res) => controlPanelAddCategory(req, res, db)); server.post('/control-panel/categories/delete', checkAuthenticated, (req, res) => controlPanelDellCategory(req, res, db)); + server.post('/control-panel/categories/update', checkAuthenticated, (req, res) => controlPanelUpdCategory(req, res, db)); + server.post('/control-panel/categories/addicon', checkAuthenticated, (req, res) => controlPanelAddIcon(req, res, db)); + server.post('/control-panel/categories/dellicon', checkAuthenticated, (req, res) => controlPanelDellIcon(req, res, db)); } diff --git a/dist/public/css/style.css b/dist/public/css/style.css index 07c22af..a42d67c 100644 --- a/dist/public/css/style.css +++ b/dist/public/css/style.css @@ -123,16 +123,14 @@ hr { background-color: #fff; } -.cart, -.pay { +.c_modal { display: flex; justify-content: center; padding-right: 10px; padding-left: 10px; } -.cart-wrap, -.pay-wrap { +.c_modal-wrap { position: fixed; background: rgba(0, 0, 0, .5); top: 0; @@ -143,14 +141,16 @@ hr { overflow-y: auto; } -.cart-content, -.pay-content { +.c_modal-content { background-color: #fff; margin-top: 7rem; padding: 25px; width: 460px; min-height: 230px; } +.c_modal-content.editCategory { + width: 760px; +} .modal-content__header .small-title { color: #404751; @@ -163,13 +163,11 @@ hr { margin-bottom: 15px; } -.cart-close, -.pay-close { +.modal-close { cursor: pointer; } -.cart-close i, -.pay-close i { +.modal-close i { color: #8d8d8d; } @@ -682,6 +680,9 @@ footer.main-footer { .admin-main { margin-top: 20px; } +.admin-main p { + margin-bottom: 1rem; +} .admin-main h2 { margin: 23px 0px 20px 0px; } diff --git a/dist/public/img/service-icons/img-1576349768467.svg b/dist/public/img/service-icons/img-1576420937629.svg similarity index 100% rename from dist/public/img/service-icons/img-1576349768467.svg rename to dist/public/img/service-icons/img-1576420937629.svg diff --git a/dist/public/img/service-icons/img-1576350274951.svg b/dist/public/img/service-icons/img-1576421960189.svg similarity index 100% rename from dist/public/img/service-icons/img-1576350274951.svg rename to dist/public/img/service-icons/img-1576421960189.svg diff --git a/dist/views/admin/assets/templates/modals/edit_category.pug b/dist/views/admin/assets/templates/modals/edit_category.pug new file mode 100644 index 0000000..2b66747 --- /dev/null +++ b/dist/views/admin/assets/templates/modals/edit_category.pug @@ -0,0 +1,36 @@ +// Edit Category +#editCategory.c_modal-wrap.hide + .container + .row + .c_modal + .c_modal-content.editCategory + .modal-content + .modal-content__header(style="margin-bottom: 1rem;") + h4.small-title Редактирование категории + span#editCategoryClose.modal-close + i.material-icons close + .modal-content__form + form(action="/control-panel/categories/update" method="POST") + .input-field.id + input(style="color: gray;" type="text" name="id" required readonly) + label ID категории + .input-field.originalTitle + input(style="color: gray;" type="text" name="originalTitle" required readonly) + label Старое название категории + .input-field.title + input(type="text" name="title" required) + label Новое название категории + .input-field.type + input(type="text" name="type" required) + label Тип аккаунтов + .input-field.img + select(name="img" id="img" required) + option(value="" disabled selected) + | Выберите иконку + label(for="img") Иконка + .input-field.format + input(type="text" name="format" required) + label Формат аккаунтов + .edit_success + button(type="submit" class="btn green waves-effect" style="width: 100%;height:50px") Редактировать +// Edit Category END diff --git a/dist/views/admin/assets/templates/modals/edit_item.pug b/dist/views/admin/assets/templates/modals/edit_item.pug new file mode 100644 index 0000000..e69de29 diff --git a/dist/views/admin/categories.pug b/dist/views/admin/categories.pug index 7aeb4a5..abee923 100644 --- a/dist/views/admin/categories.pug +++ b/dist/views/admin/categories.pug @@ -6,6 +6,13 @@ body.has-fixed-sidenav .row .col.s12 h2 Редактирование категорий + p На этой странице есть возможность добавлять новые категории, удалять старые и редактировать существующие. + .col.s12 + .card + .card-content + span.card-title Кол-во категорий + .divider + h3#categoriesCount 0 .col.s12 #categories.card .card-content @@ -14,14 +21,13 @@ body.has-fixed-sidenav .row form(id="categoryAdd" action="/control-panel/categories/create" method="POST" enctype="multipart/form-data") .input-field.col.l6.s12 - input(id="title" name="title" type="text" data-length="20" required) + input(id="title" name="title" type="text" required) label(for="title") Название категории - .file-field.input-field.col.l6.s12 - .btn.orange - span Иконка - input(type="file" name="img" required) - .file-path-wrapper - input(type="text" class="file-path" placeholder="Загрузите иконку в формате .svg") + #img.input-field.col.l6.s12 + select(name="img" id="img" required) + option(value="" disabled selected) + | Выберите иконку + label(for="img") Иконка .input-field.col.l6.s12 textarea(class="materialize-textarea" type="text" id="type" name="type" required) label(for="type") Тип аккаунтов @@ -29,9 +35,20 @@ body.has-fixed-sidenav input(type="text" id="format" name="format" required) label(for="format") Формат аккаунтов .col.s12 - button(type="submit" class="btn green") Добавить категорию + button(type="submit" class="btn") Добавить категорию .divider .under-block + .icon-add + h5 Добавить иконку + .row + form(id="iconAdd" action="/control-panel/categories/addicon" method="POST" enctype="multipart/form-data") + .file-field.input-field.col.s12 + .btn.orange + span Иконка + input(type="file" name="img" required) + .file-path-wrapper + input(type="text" class="file-path" placeholder="Загрузите иконку в формате .svg") + button(type="submit" class="btn right" style="margin-right: 10px;") Добавить .category-edit .row #edit.input-field.col.s12 @@ -39,6 +56,16 @@ body.has-fixed-sidenav option(value="" disabled selected) | Выберите категорию для редактирования label Редактирование + .icon-dell + .row + form(id="iconDell" action="/control-panel/categories/dellicon" method="POST") + #imgDell.input-field.col.s12 + select(name="img") + option(value="" disabled selected) + | Выберите иконку для удаления + label Удаление иконки + .col.s12 + button(type="submit" class="btn red ") Удалить .category-delete .row form(id="categoryDelete" action="/control-panel/categories/delete" method="POST") @@ -47,9 +74,9 @@ body.has-fixed-sidenav option(value="" disabled selected) | Выберите категорию для удаления label Удаление - .col.s6 - button(type="submit" class="btn red") Удалить + .col.s12 + button(type="submit" class="btn red ") Удалить - + include ./assets/templates/modals/edit_category.pug include ./assets/templates/footer.pug include ./assets/scripts.pug \ No newline at end of file diff --git a/dist/views/main/assets/templates/modals/cart.pug b/dist/views/main/assets/templates/modals/cart.pug index fbdecfb..4e8b54c 100644 --- a/dist/views/main/assets/templates/modals/cart.pug +++ b/dist/views/main/assets/templates/modals/cart.pug @@ -1,13 +1,13 @@ // Cart Modal -#cart.cart-wrap.hide +#cart.c_modal-wrap.hide .container .row - .cart - .cart-content + .c_modal + .c_modal-content .modal-content .modal-content__header h4.small-title Корзина - span#cartClose.cart-close + span#cartClose.modal-close i.material-icons close #modalList.modal-content__items .modal-content__info diff --git a/dist/views/main/assets/templates/modals/pay.pug b/dist/views/main/assets/templates/modals/pay.pug index f8721d2..9a22f39 100644 --- a/dist/views/main/assets/templates/modals/pay.pug +++ b/dist/views/main/assets/templates/modals/pay.pug @@ -1,13 +1,13 @@ // Pay Modal -#pay.pay-wrap.hide +#pay.c_modal-wrap.hide .container .row - .pay - .pay-content + .c_modal + .c_modal-content .modal-content .modal-content__header h4.small-title Оплатить товар - span#payClose.cart-close + span#payClose.modal-close i.material-icons close form(action='#') .modal-content__fields diff --git a/src/admin/components/categories.component.js b/src/admin/components/categories.component.js index 5fbbc0f..9e23b62 100644 --- a/src/admin/components/categories.component.js +++ b/src/admin/components/categories.component.js @@ -1,6 +1,7 @@ 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) { @@ -8,9 +9,14 @@ export class CategoriesComponent extends Component { if (this.$el) { this.$editSelect = this.$el.querySelector('#edit select'); this.$deleteSelect = this.$el.querySelector('#delete select'); + this.$iconsSelect = this.$el.querySelector('#img select'); + this.$dellIconsSelect = this.$el.querySelector('#imgDell select'); + + this.$dellIconFrom = this.$el.querySelector('#iconDell'); this.$addForm = this.$el.querySelector('#categoryAdd'); this.$deleteForm = this.$el.querySelector('#categoryDelete'); + this.$dellIconFrom.addEventListener('submit', this.dellIcon.bind(this)); this.$addForm.addEventListener('submit', this.addCategory.bind(this)); this.$deleteForm.addEventListener('submit', this.deleteCategory.bind(this)); this.$editSelect.addEventListener('change', this.insertData); @@ -19,10 +25,16 @@ export class CategoriesComponent extends Component { async init() { const categories = await apiService.getCategories(); + const icons = await apiService.getIcons(); + + const icons_html = renderIcons(icons); const html = renderCategories(categories); + document.querySelector('#categoriesCount').textContent = categories.length; this.$editSelect.insertAdjacentHTML('beforeend', html); this.$deleteSelect.insertAdjacentHTML('beforeend', html); + this.$iconsSelect.insertAdjacentHTML('beforeend', icons_html); + this.$dellIconsSelect.insertAdjacentHTML('beforeend', icons_html); M.FormSelect.init(document.querySelectorAll('select')); // Инициализируем все селекты только после их рендера! } @@ -39,6 +51,14 @@ export class CategoriesComponent extends Component { const categories = await apiService.getCategories(); const title = this.$deleteForm.elements[0].value.toLowerCase().trim(); const index = categories.map((category) => {return category.title}).indexOf(title); - index != -1 ? this.$deleteForm.submit() : alert('Выберите категорию.'); + index != -1 ? this.$deleteForm.submit() : alert('Выберите категорию для удаления.'); + } + + async dellIcon(e) { + e.preventDefault(); + const icons = await apiService.getIcons(); + const currIcon = e.target.elements[0].value; + const index = icons.indexOf(currIcon); + index != -1 ? e.target.submit() : alert('Выберите иконку для удаления.'); } } \ No newline at end of file diff --git a/src/admin/core/modal.js b/src/admin/core/modal.js index 1c54ed1..26055c6 100644 --- a/src/admin/core/modal.js +++ b/src/admin/core/modal.js @@ -3,15 +3,12 @@ export class Modal { this.$el = document.getElementById(id); this.open = document.getElementById(open); this.close = document.getElementById(close); + this.open ? this.close.addEventListener('click', this.hide.bind(this)) : false; + this.init(); } - init() { - if (this.open != null) { - this.open.addEventListener('click', openHandler.bind(this)); - this.close.addEventListener('click', closeHandler.bind(this)); - } - } + init() {} show() { this.$el.classList.remove('hide'); @@ -23,11 +20,3 @@ export class Modal { document.body.style.overflowY = ''; } } - -function openHandler() { - this.show(); -} - -function closeHandler() { - this.hide(); -} \ No newline at end of file diff --git a/src/admin/index.js b/src/admin/index.js index f6df72f..476a77d 100644 --- a/src/admin/index.js +++ b/src/admin/index.js @@ -1,6 +1,8 @@ import { MaterializeConfig } from "./config/materialize.config.js"; import { CategoriesComponent } from "./components/categories.component.js"; +import { editCategoryModal } from "./modals/editCategory.modal.js"; const categories = new CategoriesComponent('categories'); +const editCategory = new editCategoryModal('editCategory', 'edit', 'editCategoryClose'); const materialize = new MaterializeConfig(); console.log('Control-Panel v1.0.0'); \ No newline at end of file diff --git a/src/admin/modals/editCategory.modal.js b/src/admin/modals/editCategory.modal.js new file mode 100644 index 0000000..425d967 --- /dev/null +++ b/src/admin/modals/editCategory.modal.js @@ -0,0 +1,38 @@ +import { Modal } from "../core/modal"; +import { apiService } from "../services/api.service"; +import { renderIcons } from "../templates/icons.template"; + +export class editCategoryModal extends Modal { + constructor(id, open, close) { + super(id, open, close); + if (this.open) { + this.open.addEventListener('change', this.openHandler.bind(this)); + this.$el.querySelector('form').addEventListener('submit', this.updateCategory.bind(this)); + } + } + + async openHandler(e) { + this.show(); + const data = JSON.parse(e.target.value); + this.$el.querySelector('form .id input').value = data.id; + this.$el.querySelector('form .originalTitle input').value = data.title[0].toUpperCase() + data.title.slice(1); + this.$el.querySelector('form .title input').value = data.title[0].toUpperCase() + data.title.slice(1); + this.$el.querySelector('form .type input').value = data.type; + this.$el.querySelector('form .format input').value = data.format; + + const icons = await apiService.getIcons(); + const icons_html = renderIcons(icons); + this.$el.querySelector('form .img select').insertAdjacentHTML('beforeend', icons_html); + M.FormSelect.init(this.$el.querySelector('form .img select')); + 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 не существует.'); + } +} \ No newline at end of file diff --git a/src/admin/services/api.service.js b/src/admin/services/api.service.js index c1fa007..da3b544 100644 --- a/src/admin/services/api.service.js +++ b/src/admin/services/api.service.js @@ -52,6 +52,15 @@ class ApiService { console.error(err); } } + + async getIcons() { + try { + const request = `${this.url}api?token=${API_TOKEN}&icons=all`; + return useRequest(request); + } catch (err) { + console.error(err); + } + } } async function useRequest(request) { diff --git a/src/admin/templates/icons.template.js b/src/admin/templates/icons.template.js new file mode 100644 index 0000000..8fa8e29 --- /dev/null +++ b/src/admin/templates/icons.template.js @@ -0,0 +1,7 @@ +export function renderIcons(icons) { + let html = ''; + icons.forEach(icon => { + html += ``; + }); + return html; +} \ No newline at end of file