├── public ├── css │ ├── products.css │ ├── auth.css │ ├── style.min.css │ ├── addons │ │ ├── directives.min.css │ │ ├── rating.min.css │ │ ├── rating.css │ │ ├── directives.css │ │ ├── zmd.hierarchical-display.min.css │ │ ├── zmd.hierarchical-display.css │ │ ├── datatables.min.css │ │ ├── datatables-select.min.css │ │ ├── datatables-select.css │ │ └── datatables.css │ ├── home.css │ ├── style.css │ └── general.css ├── scss │ ├── _custom-styles.scss │ ├── _custom-variables.scss │ ├── addons │ │ ├── _directives.scss │ │ ├── _rating.scss │ │ ├── _hierarchical-display.scss │ │ ├── _datatables.scss │ │ └── _datatables-select.scss │ ├── free │ │ ├── _dropdowns.scss │ │ ├── _loader.scss │ │ ├── modules │ │ │ └── animations-extended │ │ │ │ └── animations-extended.scss │ │ ├── _footers.scss │ │ ├── _badges.scss │ │ ├── _depreciated.scss │ │ ├── _input-group.scss │ │ ├── _list-group.scss │ │ ├── _cards.scss │ │ ├── _switch.scss │ │ ├── _carousels.scss │ │ ├── _tables.scss │ │ ├── _pagination.scss │ │ ├── _buttons.scss │ │ ├── _navbars.scss │ │ ├── _msc.scss │ │ ├── _animations-basic.scss │ │ ├── _steppers.scss │ │ └── _treeview.scss │ ├── style.scss │ ├── _custom-skin.scss │ ├── core │ │ ├── _helpers.scss │ │ ├── _masks.scss │ │ ├── bootstrap │ │ │ └── _functions.scss │ │ ├── _typography.scss │ │ ├── _global.scss │ │ └── _waves.scss │ ├── mdb.lite.scss │ └── mdb.scss ├── img │ ├── flags.png │ ├── sample.jpg │ ├── svg │ │ ├── flags.png │ │ ├── arrow_left.svg │ │ └── arrow_right.svg │ ├── overlays │ │ ├── 01.png │ │ ├── 02.png │ │ ├── 03.png │ │ ├── 04.png │ │ ├── 05.png │ │ ├── 06.png │ │ ├── 07.png │ │ ├── 08.png │ │ └── 09.png │ ├── carousel │ │ ├── img1.jpg │ │ ├── img2.jpg │ │ └── img3.jpg │ ├── products │ │ └── default.jpg │ └── lightbox │ │ ├── preloader.gif │ │ ├── default-skin.png │ │ └── default-skin.svg ├── font │ └── roboto │ │ ├── Roboto-Bold.eot │ │ ├── Roboto-Bold.ttf │ │ ├── Roboto-Thin.eot │ │ ├── Roboto-Thin.ttf │ │ ├── Roboto-Bold.woff │ │ ├── Roboto-Bold.woff2 │ │ ├── Roboto-Light.eot │ │ ├── Roboto-Light.ttf │ │ ├── Roboto-Light.woff │ │ ├── Roboto-Light.woff2 │ │ ├── Roboto-Medium.eot │ │ ├── Roboto-Medium.ttf │ │ ├── Roboto-Medium.woff │ │ ├── Roboto-Regular.eot │ │ ├── Roboto-Regular.ttf │ │ ├── Roboto-Thin.woff │ │ ├── Roboto-Thin.woff2 │ │ ├── Roboto-Medium.woff2 │ │ ├── Roboto-Regular.woff │ │ └── Roboto-Regular.woff2 └── js │ ├── scripts.js │ ├── modules │ ├── scrolling-navbar.js │ ├── default-file-input.js │ ├── enhanced-modals.js │ ├── treeview.js │ └── bs-custom-file-input.js │ ├── cart.js │ └── addons │ ├── progressBar.min.js │ ├── progressBar.js │ ├── rating.min.js │ ├── jquery.zmd.hierarchical-display.min.js │ ├── rating.js │ └── imagesloaded.pkgd.min.js ├── .env.example ├── .gitignore ├── src ├── database │ ├── categories.json │ ├── users.json │ ├── config │ │ └── config.js │ ├── models │ │ ├── Product.js │ │ ├── OrderItem.js │ │ ├── User.js │ │ ├── Order.js │ │ └── index.js │ ├── databaseJson.js │ ├── migrations │ │ ├── 20220823165755-UserMigration.js │ │ ├── 20220823171014-ProductMigration.js │ │ ├── 20220823171705-OrderMigration.js │ │ └── 20220823172558-OrderItemMigration.js │ └── products.json ├── middlewares │ ├── menuMiddleware.js │ ├── authMiddleware.js │ ├── redirectIfAutenticated.js │ ├── userValidationsLogin.js │ ├── sessionTimeMiddleware.js │ ├── sessionMiddleware.js │ └── userValidationsRegister.js ├── routes │ ├── users.js │ ├── api.js │ ├── index.js │ ├── products.js │ └── auth.js ├── views │ ├── errors │ │ ├── 500.ejs │ │ └── 404.ejs │ ├── partials │ │ ├── scripts.ejs │ │ ├── head.ejs │ │ ├── footer.ejs │ │ ├── product.ejs │ │ ├── header.ejs │ │ └── carousel.ejs │ ├── error.ejs │ ├── index.ejs │ ├── products │ │ ├── list.ejs │ │ ├── create.ejs │ │ ├── edit.ejs │ │ └── detail.ejs │ ├── auth │ │ ├── login.ejs │ │ ├── register.ejs │ │ └── profile.ejs │ ├── order.ejs │ └── cart.ejs ├── controllers │ ├── apiController.js │ ├── homeController.js │ ├── productController.js │ └── authController.js └── app.js ├── .sequelizerc ├── package.json ├── README.md └── bin └── www /public/css/products.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | DB_USER= 2 | DB_PASS= 3 | DB_NAME= -------------------------------------------------------------------------------- /public/scss/_custom-styles.scss: -------------------------------------------------------------------------------- 1 | // Your custom styles 2 | -------------------------------------------------------------------------------- /public/scss/_custom-variables.scss: -------------------------------------------------------------------------------- 1 | // Your custom variables 2 | -------------------------------------------------------------------------------- /public/css/auth.css: -------------------------------------------------------------------------------- 1 | .login { 2 | display: grid; 3 | place-items: center; 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | yarn.lock 3 | arch_files/ 4 | template/ 5 | public/img/products 6 | .env -------------------------------------------------------------------------------- /public/img/flags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/flags.png -------------------------------------------------------------------------------- /public/img/sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/sample.jpg -------------------------------------------------------------------------------- /public/img/svg/flags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/svg/flags.png -------------------------------------------------------------------------------- /public/img/overlays/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/overlays/01.png -------------------------------------------------------------------------------- /public/img/overlays/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/overlays/02.png -------------------------------------------------------------------------------- /public/img/overlays/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/overlays/03.png -------------------------------------------------------------------------------- /public/img/overlays/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/overlays/04.png -------------------------------------------------------------------------------- /public/img/overlays/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/overlays/05.png -------------------------------------------------------------------------------- /public/img/overlays/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/overlays/06.png -------------------------------------------------------------------------------- /public/img/overlays/07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/overlays/07.png -------------------------------------------------------------------------------- /public/img/overlays/08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/overlays/08.png -------------------------------------------------------------------------------- /public/img/overlays/09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/overlays/09.png -------------------------------------------------------------------------------- /src/database/categories.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"id": 1, "name": "Impresa"}, 3 | {"id": 2, "name": "Digital"} 4 | ] -------------------------------------------------------------------------------- /public/img/carousel/img1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/carousel/img1.jpg -------------------------------------------------------------------------------- /public/img/carousel/img2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/carousel/img2.jpg -------------------------------------------------------------------------------- /public/img/carousel/img3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/carousel/img3.jpg -------------------------------------------------------------------------------- /public/img/products/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/products/default.jpg -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Bold.eot -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Bold.ttf -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Thin.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Thin.eot -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Thin.ttf -------------------------------------------------------------------------------- /public/img/lightbox/preloader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/lightbox/preloader.gif -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Bold.woff -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Bold.woff2 -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Light.eot -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Light.ttf -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Light.woff -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Light.woff2 -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Medium.eot -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Medium.ttf -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Medium.woff -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Regular.eot -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Regular.ttf -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Thin.woff -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Thin.woff2 -------------------------------------------------------------------------------- /public/img/lightbox/default-skin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/img/lightbox/default-skin.png -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Medium.woff2 -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Regular.woff -------------------------------------------------------------------------------- /public/font/roboto/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargilla/node-ecommerce/HEAD/public/font/roboto/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /public/scss/addons/_directives.scss: -------------------------------------------------------------------------------- 1 | // Optional directives 2 | @each $key in (0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100) { 3 | .opacity-#{$key} { 4 | opacity: $key * .01; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/middlewares/menuMiddleware.js: -------------------------------------------------------------------------------- 1 | module.exports = (req, res, next) => { 2 | res.locals.active = req.path.split("/")[1]; // [0] will be empty since routes start with '/' 3 | next(); 4 | }; 5 | -------------------------------------------------------------------------------- /public/scss/free/_dropdowns.scss: -------------------------------------------------------------------------------- 1 | // Dropdowns 2 | .dropdown { 3 | .dropdown-menu { 4 | .dropdown-item { 5 | &:active { 6 | background-color: $grey-darken-1; 7 | } 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /public/scss/free/_loader.scss: -------------------------------------------------------------------------------- 1 | // Loader / Spinner 2 | .fast { 3 | &.spinner-border { 4 | animation: $spinner-border-animation; 5 | } 6 | &.spinner-grow { 7 | animation: $spinner-grow-animation; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/middlewares/authMiddleware.js: -------------------------------------------------------------------------------- 1 | authMiddleware = (req, res, next) => { 2 | if (!req.session.userLogged) { 3 | return res.redirect("/login"); 4 | } 5 | 6 | next(); 7 | }; 8 | 9 | module.exports = authMiddleware; 10 | -------------------------------------------------------------------------------- /public/img/svg/arrow_left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/img/svg/arrow_right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/middlewares/redirectIfAutenticated.js: -------------------------------------------------------------------------------- 1 | redirectIfAutenticated = (req, res, next) => { 2 | if (req.session.userLogged) { 3 | return res.redirect("/profile"); 4 | } 5 | 6 | next(); 7 | }; 8 | 9 | module.exports = redirectIfAutenticated; 10 | -------------------------------------------------------------------------------- /public/css/style.min.css: -------------------------------------------------------------------------------- 1 | .view,body,html{height:100%}.carousel{height:50%}.carousel .carousel-inner,.carousel .carousel-inner .active,.carousel .carousel-inner .carousel-item{height:100%}@media (max-width:776px){.carousel{height:100%}}.page-footer{background-color:#929FBA} -------------------------------------------------------------------------------- /public/scss/free/modules/animations-extended/animations-extended.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * MDBootstrap Animations Extended 3 | * Learn more: https://mdbootstrap.com/docs/jquery/css/animations/ 4 | * About MDBootstrap: https://mdbootstrap.com/ 5 | */ 6 | 7 | @import "module"; 8 | -------------------------------------------------------------------------------- /src/routes/users.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /public/css/addons/directives.min.css: -------------------------------------------------------------------------------- 1 | .opacity-0{opacity:0}.opacity-10{opacity:.1}.opacity-20{opacity:.2}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.opacity-100{opacity:1} -------------------------------------------------------------------------------- /src/middlewares/userValidationsLogin.js: -------------------------------------------------------------------------------- 1 | const { check } = require("express-validator"); 2 | 3 | const validations = [ 4 | check("email").isEmail().withMessage("El email es invalido"), 5 | check("password", "Password invalido").notEmpty(), 6 | ]; 7 | 8 | module.exports = validations; 9 | -------------------------------------------------------------------------------- /src/routes/api.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | const controller = require("../controllers/apiController"); 5 | 6 | router.get("/product/:id", controller.product); 7 | router.post("/checkout", controller.checkout); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /public/css/addons/rating.min.css: -------------------------------------------------------------------------------- 1 | .mdb-rating .rate-popover{color:grey}.mdb-rating .live{color:#000}.mdb-rating .oneStar{color:#44370f}.mdb-rating .twoStars{color:#96781e}.mdb-rating .threeStars{color:#e2b52e}.mdb-rating .fourStars{color:#f1ba12}.mdb-rating .fiveStars{color:#f3cb06}.mdb-rating .amber-text{color:#ffc107!important} -------------------------------------------------------------------------------- /.sequelizerc: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | config: path.resolve('./src/database/config', 'config.js'), 5 | 'models-path': path.resolve('./src/database/models'), 6 | 'seeders-path': path.resolve('./src/database/seeders'), 7 | 'migrations-path': path.resolve('./src/database/migrations'), 8 | } -------------------------------------------------------------------------------- /public/js/scripts.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function () { 2 | /* Animations initialization */ 3 | new WOW().init(); 4 | 5 | /* Toastr Initialization */ 6 | toastr.options = { 7 | positionClass: "toast-bottom-right", 8 | fadeIn: 300, 9 | fadeOut: 1000, 10 | timeOut: 5000, 11 | extendedTimeOut: 1000, 12 | }; 13 | }); 14 | -------------------------------------------------------------------------------- /src/views/errors/500.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- include('../partials/head') %> 6 | 7 | 8 | 9 | <%- include('../partials/header') %> 10 |
11 |
12 |

Ocurrió un error

13 |
14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /src/views/errors/404.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- include('../partials/head') %> 6 | 7 | 8 | 9 | <%- include('../partials/header') %> 10 |
11 |
12 |

Pagina no encontrada

13 |
14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /public/css/home.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | header, 4 | .carousel { 5 | height: 60vh; 6 | } 7 | 8 | @media (max-width: 740px) { 9 | html, 10 | body, 11 | header, 12 | .carousel { 13 | height: 100vh; 14 | } 15 | } 16 | 17 | @media (min-width: 800px) and (max-width: 850px) { 18 | html, 19 | body, 20 | header, 21 | .carousel { 22 | height: 100vh; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/database/users.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 2, 4 | "email": "juan@juan.com", 5 | "password": "$2a$10$dyX8uOZQCiP6AoQfWCuapuiAyxCcA2CCF7SDSmdh.VAuSIA/RKXjW", 6 | "img": "" 7 | }, 8 | { 9 | "id": 3, 10 | "email": "sargilla@gmail.com", 11 | "password": "$2a$10$51r5wHisvhqgxQGteCpDi.GjGsEwY29A3OjvHjZe9QR4DhzY6uz3W", 12 | "img": "" 13 | } 14 | ] -------------------------------------------------------------------------------- /public/scss/free/_footers.scss: -------------------------------------------------------------------------------- 1 | // Footers 2 | footer { 3 | &.page-footer { 4 | bottom: 0; 5 | color: $white-base; 6 | .container-fluid { 7 | width: auto; 8 | } 9 | .footer-copyright { 10 | overflow: hidden; 11 | color: $footer-copyright-color; 12 | background-color: $footer-copyright-bg-color; 13 | } 14 | a { 15 | color: $white-base; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /public/scss/free/_badges.scss: -------------------------------------------------------------------------------- 1 | // Badges 2 | .badge { 3 | color: $white !important; 4 | border-radius: $border-radius-base; 5 | box-shadow: $z-depth-1; 6 | } 7 | .badge-pill { 8 | padding-right: $badge-pill-padding-x; 9 | padding-left: $badge-pill-padding-x; 10 | border-radius: $badge-pill-border-radius; 11 | } 12 | @each $name, $color in $basic-mdb-colors { 13 | @include make-badge($name, $color); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | const controller = require("../controllers/homeController"); 5 | const authMiddleware = require("../middlewares/authMiddleware"); 6 | /* GET home page. */ 7 | router.get("/", controller.home); 8 | router.get("/cart", authMiddleware, controller.cart); 9 | router.get("/order/:id", authMiddleware, controller.order); 10 | 11 | module.exports = router; 12 | -------------------------------------------------------------------------------- /public/scss/free/_depreciated.scss: -------------------------------------------------------------------------------- 1 | // These settings will be only for one version 2 | // Scrolable navbar 3 | 4 | /* 5 | .navbar { 6 | &.fixed-top, 7 | &.sticky-top { 8 | .navbar-collapse { 9 | @media (min-width: 400px) and (max-width: 767px), 10 | (min-width: 800px) and (max-width: 850px) { 11 | max-height: 340px; 12 | overflow-x: hidden; 13 | overflow-y: auto; 14 | } 15 | } 16 | } 17 | } 18 | */ 19 | -------------------------------------------------------------------------------- /public/css/addons/rating.css: -------------------------------------------------------------------------------- 1 | .mdb-rating .rate-popover { 2 | color: grey; } 3 | 4 | .mdb-rating .live { 5 | color: black; } 6 | 7 | .mdb-rating .oneStar { 8 | color: #44370f; } 9 | 10 | .mdb-rating .twoStars { 11 | color: #96781e; } 12 | 13 | .mdb-rating .threeStars { 14 | color: #e2b52e; } 15 | 16 | .mdb-rating .fourStars { 17 | color: #f1ba12; } 18 | 19 | .mdb-rating .fiveStars { 20 | color: #f3cb06; } 21 | 22 | .mdb-rating .amber-text { 23 | color: #ffc107 !important; } -------------------------------------------------------------------------------- /public/js/modules/scrolling-navbar.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | (function ($) { 4 | var SCROLLING_NAVBAR_OFFSET_TOP = 50; 5 | $(window).on('scroll', function () { 6 | var $navbar = $('.navbar'); 7 | 8 | if ($navbar.length) { 9 | if ($navbar.offset().top > SCROLLING_NAVBAR_OFFSET_TOP) { 10 | $('.scrolling-navbar').addClass('top-nav-collapse'); 11 | } else { 12 | $('.scrolling-navbar').removeClass('top-nav-collapse'); 13 | } 14 | } 15 | }); 16 | })(jQuery); -------------------------------------------------------------------------------- /public/js/cart.js: -------------------------------------------------------------------------------- 1 | function setCarritoVacio() { 2 | cartRows.innerHTML = ` 3 | 4 |
No tienes products en el carrito
5 | 6 | `; 7 | } 8 | function vaciarCarrito() { 9 | localStorage.removeItem("carrito"); 10 | } 11 | 12 | function calcularTotal(products) { 13 | return products.reduce( 14 | (acum, product) => (acum += product.price * product.quantity), 15 | 0 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /public/scss/addons/_rating.scss: -------------------------------------------------------------------------------- 1 | .mdb-rating { 2 | 3 | .rate-popover { 4 | color: #808080; 5 | } 6 | 7 | .live { 8 | color: #000; 9 | } 10 | 11 | .oneStar { 12 | color: #44370f; 13 | } 14 | 15 | .twoStars { 16 | color: #96781e; 17 | } 18 | 19 | .threeStars { 20 | color: #e2b52e; 21 | } 22 | 23 | .fourStars { 24 | color: #f1ba12; 25 | } 26 | 27 | .fiveStars { 28 | color: #f3cb06; 29 | } 30 | 31 | .amber-text { 32 | color: #ffc107; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /public/css/addons/directives.css: -------------------------------------------------------------------------------- 1 | .opacity-0 { 2 | opacity: 0; } 3 | 4 | .opacity-10 { 5 | opacity: 0.1; } 6 | 7 | .opacity-20 { 8 | opacity: 0.2; } 9 | 10 | .opacity-30 { 11 | opacity: 0.3; } 12 | 13 | .opacity-40 { 14 | opacity: 0.4; } 15 | 16 | .opacity-50 { 17 | opacity: 0.5; } 18 | 19 | .opacity-60 { 20 | opacity: 0.6; } 21 | 22 | .opacity-70 { 23 | opacity: 0.7; } 24 | 25 | .opacity-80 { 26 | opacity: 0.8; } 27 | 28 | .opacity-90 { 29 | opacity: 0.9; } 30 | 31 | .opacity-100 { 32 | opacity: 1; } -------------------------------------------------------------------------------- /public/scss/free/_input-group.scss: -------------------------------------------------------------------------------- 1 | // Input group 2 | .md-form { 3 | &.input-group { 4 | label { 5 | top: 0; 6 | margin-bottom: 0; 7 | } 8 | .input-group-text { 9 | background-color: $input-group-text-bgc; 10 | &.md-addon { 11 | font-weight: 500; 12 | background-color: transparent; 13 | border: none; 14 | } 15 | } 16 | .form-control { 17 | padding: $input-group-form-control-py $input-group-form-control-px; 18 | margin: 0; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/views/partials/scripts.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/views/error.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- include('../partials/head') %> 6 | 7 | 8 | 9 | <%- include('../partials/header') %> 10 |
11 |
12 |

13 | <%= message %> 14 |

15 |

16 | <%= error.status %> 17 |

18 |
<%= error.stack %>
19 |
20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /src/controllers/apiController.js: -------------------------------------------------------------------------------- 1 | const db = require("../database/models"); 2 | 3 | module.exports = { 4 | product: async function (req, res) { 5 | let product = await db.Product.findByPk(req.params.id); 6 | return res.json(product); 7 | }, 8 | checkout: async function (req, res) { 9 | // return res.send({ ...req.body, userId: req.session.userLogged.id }); 10 | let order = await db.Order.create( 11 | { ...req.body, userId: req.session.userLogged.id }, 12 | { 13 | include: db.Order.OrderItems, 14 | } 15 | ); 16 | res.json({ ok: true, status: 200, order: order }); 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "magazine", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "nodemon ./bin/www" 7 | }, 8 | "dependencies": { 9 | "bcryptjs": "^2.4.3", 10 | "cookie-parser": "~1.4.4", 11 | "debug": "~2.6.9", 12 | "dotenv": "^16.0.1", 13 | "ejs": "~2.6.1", 14 | "express": "~4.16.1", 15 | "express-session": "^1.17.3", 16 | "express-validator": "^6.14.1", 17 | "http-errors": "~1.6.3", 18 | "method-override": "^3.0.0", 19 | "morgan": "~1.9.1", 20 | "multer": "^1.4.5-lts.1", 21 | "mysql2": "^2.3.3", 22 | "sequelize": "^6.21.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/controllers/homeController.js: -------------------------------------------------------------------------------- 1 | const db = require("../database/models"); 2 | module.exports = { 3 | home: async function (req, res) { 4 | let products = await db.Product.findAll({ 5 | where: { 6 | marked: 1, 7 | }, 8 | }); 9 | 10 | return res.render("index", { title: "E-Commerce", products }); 11 | }, 12 | cart: function (req, res) { 13 | return res.render("cart"); 14 | }, 15 | order: async function (req, res) { 16 | let order = await db.Order.findByPk(req.params.id, { 17 | include: db.Order.OrderItems, 18 | }); 19 | // res.send(order); 20 | return res.render("order", { order }); 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /public/js/modules/default-file-input.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function ($) { 4 | $('.input-default-wrapper').on('change', '.input-default-js', function (e) { 5 | 6 | var $this = $(e.target), 7 | $label = $this.next('label'), 8 | $files = $this[0].files; 9 | var fileName = ''; 10 | 11 | if ($files && $files.length > 1) { 12 | fileName = ($this.attr('data-multiple-target') || '').replace('{target}', $files.length); 13 | } else if (e.target.value) { 14 | fileName = e.target.value.split('\\').pop(); 15 | } 16 | fileName ? $label.find('.span-choose-file').html(fileName) : $label.html($label.html()); 17 | }); 18 | })(jQuery); -------------------------------------------------------------------------------- /src/database/config/config.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | module.exports = { 3 | development: { 4 | username: process.env.DB_USER, 5 | password: process.env.DB_PASS, 6 | database: process.env.DB_NAME, 7 | host: "127.0.0.1", 8 | dialect: "mysql", 9 | }, 10 | test: { 11 | username: process.env.DB_USER, 12 | password: process.env.DB_PASS, 13 | database: process.env.DB_NAME, 14 | host: "127.0.0.1", 15 | dialect: "mysql", 16 | }, 17 | production: { 18 | username: process.env.DB_USER, 19 | password: process.env.DB_PASS, 20 | database: process.env.DB_NAME, 21 | host: "127.0.0.1", 22 | dialect: "mysql", 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /src/views/partials/head.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Node E-Commerce 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /public/scss/style.scss: -------------------------------------------------------------------------------- 1 | // Your custom styles 2 | 3 | /* Required height of parents of the Half Page Carousel for proper displaying carousel itself */ 4 | html, 5 | body, 6 | .view { 7 | height: 100%; } 8 | 9 | /* Half Page Carousel itself*/ 10 | .carousel { 11 | height: 50%; } 12 | .carousel .carousel-inner { 13 | height: 100%; } 14 | .carousel .carousel-inner .carousel-item, 15 | .carousel .carousel-inner .active { 16 | height: 100%; } 17 | 18 | /* Adjustment for mobile devices*/ 19 | @media (max-width: 776px) { 20 | .carousel { 21 | height: 100%; } } 22 | 23 | /* Footer color for sake of consistency with Navbar */ 24 | .page-footer { 25 | background-color: #929FBA; } 26 | -------------------------------------------------------------------------------- /public/scss/free/_list-group.scss: -------------------------------------------------------------------------------- 1 | // List group 2 | .media { 3 | .media-left { 4 | padding: $list-group-padding; 5 | 6 | img { 7 | box-shadow: $z-depth-1; 8 | } 9 | } 10 | } 11 | 12 | .list-group { 13 | .list-group-item { 14 | &:first-child { 15 | border-top-left-radius: $border-radius-base; 16 | border-top-right-radius: $border-radius-base; 17 | } 18 | 19 | &:last-child { 20 | border-bottom-right-radius: $border-radius-base; 21 | border-bottom-left-radius: $border-radius-base; 22 | } 23 | } 24 | 25 | a, 26 | button { 27 | transition: $list-group-transition; 28 | 29 | &:hover { 30 | transition: $list-group-transition; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /public/scss/free/_cards.scss: -------------------------------------------------------------------------------- 1 | // Cards 2 | .card { 3 | font-weight: 400; 4 | border: 0; 5 | box-shadow: $z-depth-1; 6 | &[class*="border"] { 7 | border: 1px solid $grey-base; 8 | box-shadow: none; 9 | } 10 | .card-body { 11 | h1, h2, h3, h4, h5, h6 { 12 | font-weight: 400; 13 | } 14 | .card-title { 15 | a { 16 | transition: $md-card-link-transition; 17 | &:hover { 18 | transition: $md-card-link-transition; 19 | } 20 | } 21 | } 22 | .card-text { 23 | font-size: $md-card-font-size; 24 | font-weight: 400; 25 | color: $md-card-text-color; 26 | } 27 | } 28 | .md-form { 29 | label { 30 | font-weight: 300; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/middlewares/sessionTimeMiddleware.js: -------------------------------------------------------------------------------- 1 | module.exports = (req, res, next) => { 2 | let dateNow = Date.now(); 3 | console.log("dateNow", dateNow); 4 | console.log("lastActivity:", req.session.lastActitity); 5 | // reviso si es un usuario 6 | if (req.session.userLogged && req.session.lastActitity) { 7 | // resto las fecha en formato numerico 8 | let compare = dateNow - req.session.lastActitity; 9 | // console.log("fechas comparadas", compare); 10 | // si es mayor a 30 min, redirijo al login 11 | if (compare > 1000 * 60 * 30) { 12 | req.session.destroy(); 13 | return res.redirect("/login"); 14 | } 15 | // sino actualizo la fecha en formato numerico 16 | req.session.lastActitity = dateNow; 17 | } 18 | return next(); 19 | }; 20 | -------------------------------------------------------------------------------- /src/database/models/Product.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, dataTypes) => { 2 | let alias = "Product"; 3 | let columns = { 4 | id: { 5 | type: dataTypes.INTEGER(11), 6 | primaryKey: true, 7 | autoIncrement: true, 8 | allowNull: false, 9 | }, 10 | name: { 11 | type: dataTypes.STRING(100), 12 | allowNull: false, 13 | }, 14 | img: { 15 | type: dataTypes.STRING(100), 16 | allowNull: true, 17 | }, 18 | price: { 19 | type: dataTypes.DECIMAL(10, 2), 20 | allowNull: false, 21 | }, 22 | marked: { 23 | type: dataTypes.BOOLEAN, 24 | defaultValue: false, 25 | }, 26 | }; 27 | 28 | let configurations = {}; 29 | 30 | const Product = sequelize.define(alias, columns, configurations); 31 | 32 | return Product; 33 | }; 34 | -------------------------------------------------------------------------------- /public/js/modules/enhanced-modals.js: -------------------------------------------------------------------------------- 1 | /* 2 | Enhanced Bootstrap Modals 3 | https://mdbootstrap.com 4 | office@mdbootstrap.com 5 | */ 6 | 7 | (function($){ 8 | $('body').on('shown.bs.modal', '.modal', function() { 9 | if(!$('.modal-backdrop').length) { 10 | 11 | $modal_dialog = $(this).children('.modal-dialog') 12 | 13 | if($modal_dialog.hasClass('modal-side')) { 14 | $(this).addClass('modal-scrolling'); 15 | $('body').addClass('scrollable'); 16 | } 17 | 18 | if($modal_dialog.hasClass('modal-frame')) { 19 | $(this).addClass('modal-content-clickable'); 20 | $('body').addClass('scrollable'); 21 | } 22 | } 23 | }); 24 | $('body').on('hidden.bs.modal', '.modal', function() { 25 | $('body').removeClass('scrollable'); 26 | }); 27 | })(jQuery); 28 | -------------------------------------------------------------------------------- /public/scss/_custom-skin.scss: -------------------------------------------------------------------------------- 1 | // Your custom skin 2 | // Skins 3 | $skins: () !default; 4 | $skins: map-merge(( 5 | "test": ( 6 | "skin-primary-color": #fff, 7 | "skin-navbar": #fff, 8 | "skin-footer-color": #fff, 9 | "skin-flat": #fff, 10 | "skin-accent": #fff, 11 | "skin-sidenav-item": #fff, 12 | "skin-sidenav-item-hover": #fff, 13 | "skin-gradient-start": #fff, 14 | "skin-gradient-end": #fff, 15 | "skin-mask-slight": #fff, 16 | "skin-mask-light": #fff, 17 | "skin-mask-strong": #fff, 18 | "skin-sn-child": #fff, 19 | "skin-btn-primary": #fff, 20 | "skin-btn-secondary": #fff, 21 | "skin-btn-default": #fff, 22 | "skin-text": #fff 23 | ) 24 | ), $skins); 25 | 26 | -------------------------------------------------------------------------------- /src/database/databaseJson.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | module.exports = { 5 | readJson: function (filename) { 6 | const pJson = fs.readFileSync(path.join(__dirname, filename)); 7 | try { 8 | const elements = JSON.parse(pJson) 9 | return elements 10 | } catch (e) { 11 | console.log(`el archivo ${pJson} puede estar vacio`) 12 | return [] 13 | } 14 | }, 15 | writeJson: function (dataArray, filename) { 16 | const data = JSON.stringify(dataArray, null, 4) 17 | const pJson = fs.writeFileSync(path.join(__dirname, filename), data); 18 | return pJson 19 | }, 20 | lastElementId: function(array) { 21 | if (array.length == 0) { return 0 } 22 | return Math.max(...array.map(ele => ele.id)) 23 | }, 24 | } -------------------------------------------------------------------------------- /src/middlewares/sessionMiddleware.js: -------------------------------------------------------------------------------- 1 | const db = require("../database/models"); 2 | const Users = db.User; 3 | 4 | sessionMiddleware = async (req, res, next) => { 5 | res.locals.userFound = false; 6 | let userFromCookie; 7 | 8 | if (req.session && req.session.userLogged) { 9 | res.locals.userFound = true; 10 | res.locals.userLogged = req.session.userLogged; 11 | if (req.session.userLogged.id === 1) { 12 | res.locals.userAdmin = true; 13 | } 14 | } else { 15 | if (req.cookies.userId) { 16 | userFromCookie = await Users.findOne({ 17 | where: { id: req.cookies.userId }, 18 | }); 19 | } 20 | 21 | if (userFromCookie) { 22 | res.locals.userFound = true; 23 | res.locals.userLogged = req.session.userLogged = userFromCookie; 24 | } 25 | } 26 | 27 | next(); 28 | }; 29 | 30 | module.exports = sessionMiddleware; 31 | -------------------------------------------------------------------------------- /public/scss/addons/_hierarchical-display.scss: -------------------------------------------------------------------------------- 1 | .zmd-hierarchical-display { 2 | visibility: hidden; 3 | &.in { 4 | visibility: visible; 5 | } 6 | } 7 | .zmd-hierarchical-displaying { 8 | visibility: visible; 9 | } 10 | 11 | .animation { 12 | animation-duration: 1s; 13 | animation-fill-mode: both; 14 | } 15 | 16 | .animation.zoomedIn, 17 | .animation.zoomedOut { 18 | animation-timing-function: cubic-bezier(.55, 0, .1, 1); // "Swift Out" easing curve 19 | } 20 | 21 | @keyframes zoomedIn { 22 | from { 23 | transform: scale(0); 24 | } 25 | to { 26 | transform: scale(1); 27 | } 28 | } 29 | 30 | @keyframes zoomedOut { 31 | from { 32 | transform: scale(1); 33 | } 34 | to { 35 | transform: scale(0); 36 | } 37 | } 38 | 39 | .zoomedIn { 40 | animation-name: zoomedIn; 41 | } 42 | 43 | .zoomedOut { 44 | animation-name: zoomedOut; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # E-Commerce 2 | 3 | Proyecto de un E-Commerce en Nodejs, con ejs, base de datos Mysql con Sequelize. 4 | 5 | Este proyecto cuenta con un ABM de productos, registro y login de usuarios, middlewares para rutas de usuario y es un boilerplate para empezar a desarrollar un carrito de compras 6 | 7 | ## Instalación 8 | 9 | Clonar el repo 10 | 11 | ``` 12 | git clone git@github.com:sargilla/node-ecommerce.git 13 | ``` 14 | 15 | Instalar paquetes 16 | 17 | ``` 18 | npm install 19 | ``` 20 | 21 | Crear .env para las variables de entorno 22 | 23 | ``` 24 | cp .env.example .env 25 | ``` 26 | 27 | Crear la base de datos y configurar el .env con los datos de su conexión 28 | 29 | ``` 30 | DB_USER= 31 | DB_PASS= 32 | DB_NAME= 33 | ``` 34 | 35 | Ejecutar la migración para crear las tablas en la DB_NAME 36 | 37 | ``` 38 | sequelize db:migrate 39 | ``` 40 | 41 | Inicializar el proyecto 42 | 43 | ``` 44 | npm start 45 | ``` 46 | -------------------------------------------------------------------------------- /public/js/addons/progressBar.min.js: -------------------------------------------------------------------------------- 1 | (function($){$.fn.progressBar=function(givenValue){const $this=$(this);function init(selector){const progressValue=selector.children().attr('aria-valuenow');selector.children().width(`${progressValue}%`);selector.children().html('');$this.hasClass('md-progress')?selector.children().children().addClass('md-progress-bar-text'):selector.children().children().addClass('progress-bar-text');(progressValue!==100)?selector.children().children().text(`${progressValue}%`):selector.children().children().html('')} 2 | function set(selector,value){selector.children().removeClass('success fail active');selector.children().attr('aria-valuenow',value);init(selector);if(value>100){return!1}else if(value===100){selector.children().addClass('success')}else if(value<30){selector.children().addClass('fail')}else{selector.children().addClass('active')} 3 | return!0} 4 | set($this,givenValue)}}(jQuery)) -------------------------------------------------------------------------------- /src/database/models/OrderItem.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, dataTypes) => { 2 | let alias = "OrderItem"; 3 | let columns = { 4 | id: { 5 | type: dataTypes.INTEGER(11), 6 | primaryKey: true, 7 | autoIncrement: true, 8 | allowNull: false, 9 | }, 10 | 11 | name: { 12 | type: dataTypes.STRING(100), 13 | allowNull: false, 14 | }, 15 | price: { 16 | type: dataTypes.DECIMAL(10, 2), 17 | allowNull: false, 18 | }, 19 | quantity: { 20 | type: dataTypes.INTEGER(11), 21 | allowNull: false, 22 | }, 23 | }; 24 | let configurations = {}; 25 | 26 | const OrderItem = sequelize.define(alias, columns, configurations); 27 | 28 | OrderItem.associate = (models) => { 29 | OrderItem.belongsTo(models.Order, { 30 | as: "order", 31 | }); 32 | 33 | OrderItem.belongsTo(models.Product, { 34 | as: "product", 35 | }); 36 | }; 37 | 38 | return OrderItem; 39 | }; 40 | -------------------------------------------------------------------------------- /src/database/models/User.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, dataTypes) => { 2 | let alias = "User"; 3 | let columns = { 4 | id: { 5 | type: dataTypes.INTEGER(11), 6 | primaryKey: true, 7 | autoIncrement: true, 8 | allowNull: false, 9 | }, 10 | name: { 11 | type: dataTypes.STRING(50), 12 | allowNull: false, 13 | }, 14 | 15 | email: { 16 | type: dataTypes.STRING(100), 17 | allowNull: false, 18 | }, 19 | password: { 20 | type: dataTypes.STRING(100), 21 | allowNull: false, 22 | }, 23 | img: { 24 | type: dataTypes.STRING(100), 25 | allowNull: true, 26 | }, 27 | }; 28 | let configurations = {}; 29 | 30 | const User = sequelize.define(alias, columns, configurations); 31 | 32 | User.associate = (models) => { 33 | User.hasMany(models.Order, { 34 | as: "orders", 35 | foreignKey: "userId", 36 | }); 37 | }; 38 | 39 | return User; 40 | }; 41 | -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | /* Required height of parents of the Half Page Carousel for proper displaying carousel itself */ 2 | html, 3 | body, 4 | .view { 5 | height: 100%; 6 | } 7 | 8 | table.table td, 9 | table.table th { 10 | vertical-align: middle; 11 | } 12 | /* Half Page Carousel itself*/ 13 | .carousel { 14 | height: 50%; 15 | } 16 | .carousel .carousel-inner { 17 | height: 100%; 18 | } 19 | .carousel .carousel-inner .carousel-item, 20 | .carousel .carousel-inner .active { 21 | height: 100%; 22 | } 23 | 24 | /* Adjustment for mobile devices*/ 25 | @media (max-width: 776px) { 26 | .carousel { 27 | height: 100%; 28 | } 29 | } 30 | 31 | /* Footer color for sake of consistency with Navbar */ 32 | .page-footer { 33 | background-color: #929fba; 34 | } 35 | 36 | .cart-number { 37 | position: absolute; 38 | top: 0; 39 | padding-top: 6px; 40 | } 41 | 42 | main.error { 43 | height: 100%; 44 | display: grid; 45 | place-items: center; 46 | } 47 | 48 | main.error h1 { 49 | text-align: center; 50 | } 51 | -------------------------------------------------------------------------------- /src/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- include('./partials/head') %> 6 | 7 | 8 | 9 | 10 | <%- include('./partials/header') %> 11 | 12 | <%- include('./partials/carousel') %> 13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 | 21 |
22 | 23 | 24 |
25 | 26 | <% products.forEach( prod=> { %> 27 | <%- include('./partials/product' ,{prod}) %> 28 | 29 | <% } ) %> 30 | 31 |
32 | 33 | 34 | 35 | 36 |
37 | 38 | 39 |
40 |
41 | 42 | 43 | <%- include('./partials/footer') %> 44 | 45 | <%- include('./partials/scripts') %> 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/database/models/Order.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, dataTypes) => { 2 | let alias = "Order"; 3 | let columns = { 4 | id: { 5 | type: dataTypes.INTEGER(11), 6 | primaryKey: true, 7 | autoIncrement: true, 8 | allowNull: false, 9 | }, 10 | // userId: { 11 | // type: dataTypes.INTEGER(11), 12 | // allowNull: false, 13 | // }, 14 | total: { 15 | type: dataTypes.DECIMAL(10, 2), 16 | allowNull: false, 17 | }, 18 | paymentMethod: { 19 | type: dataTypes.STRING(25), 20 | allowNull: false, 21 | }, 22 | shippingMethod: { 23 | type: dataTypes.STRING(25), 24 | allowNull: true, 25 | }, 26 | }; 27 | let configurations = {}; 28 | 29 | const Order = sequelize.define(alias, columns, configurations); 30 | 31 | Order.associate = (models) => { 32 | Order.User = Order.belongsTo(models.User, { 33 | as: "user", 34 | foreignKey: "userId", 35 | }); 36 | Order.OrderItems = Order.hasMany(models.OrderItem, { 37 | as: "orderItems", 38 | }); 39 | }; 40 | 41 | return Order; 42 | }; 43 | -------------------------------------------------------------------------------- /src/views/products/list.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- include('../partials/head') %> 6 | 7 | 8 | 9 | 10 | <%- include('../partials/header') %> 11 |
12 |
13 |

Nuestros Productos

14 | Agregar Producto 15 |
16 |
17 | <% products.forEach( prod=> { %> 18 | <%- include('../partials/product' ,{prod}) %> 19 | <% } ) %> 20 |
21 |
22 | 23 | 24 |
25 | 26 |
27 | <%- include('../partials/footer') %> 28 | <%- include('../partials/scripts') %> 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /public/scss/free/_switch.scss: -------------------------------------------------------------------------------- 1 | // Switch free 2 | .bs-switch { 3 | position: relative; 4 | display: inline-block; 5 | width: 60px; 6 | height: 34px; 7 | input { 8 | display: none; 9 | &:checked { 10 | + .slider { 11 | background-color: #2196F3; 12 | &:before { 13 | transform: translateX(26px); 14 | } 15 | } 16 | } 17 | &:focus { 18 | + .slider { 19 | box-shadow: 0 0 1px #2196F3; 20 | } 21 | } 22 | } 23 | .slider { 24 | position: absolute; 25 | cursor: pointer; 26 | top: 0; 27 | left: 0; 28 | right: 0; 29 | bottom: 0; 30 | background-color: #ccc; 31 | -webkit-transition: .4s; 32 | transition: .4s; 33 | &:before { 34 | position: absolute; 35 | content: ""; 36 | height: 26px; 37 | width: 26px; 38 | left: 4px; 39 | bottom: 4px; 40 | background-color: white; 41 | -webkit-transition: .4s; 42 | transition: .4s; 43 | } 44 | &.round { 45 | border-radius: 34px; 46 | &:before { 47 | border-radius: 50%; 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/database/models/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const Sequelize = require('sequelize'); 6 | const basename = path.basename(__filename); 7 | const env = process.env.NODE_ENV || 'development'; 8 | const config = require(__dirname + '/../config/config.js')[env]; 9 | const db = {}; 10 | 11 | let sequelize; 12 | if (config.use_env_variable) { 13 | sequelize = new Sequelize(process.env[config.use_env_variable], config); 14 | } else { 15 | sequelize = new Sequelize(config.database, config.username, config.password, config); 16 | } 17 | 18 | fs 19 | .readdirSync(__dirname) 20 | .filter(file => { 21 | return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); 22 | }) 23 | .forEach(file => { 24 | const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes); 25 | db[model.name] = model; 26 | }); 27 | 28 | Object.keys(db).forEach(modelName => { 29 | if (db[modelName].associate) { 30 | db[modelName].associate(db); 31 | } 32 | }); 33 | 34 | db.sequelize = sequelize; 35 | db.Sequelize = Sequelize; 36 | 37 | module.exports = db; 38 | -------------------------------------------------------------------------------- /src/routes/products.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const path = require("path"); 3 | const router = express.Router(); 4 | const multer = require("multer"); 5 | 6 | const controller = require("../controllers/productController"); 7 | 8 | const storage = multer.diskStorage({ 9 | destination: function (req, file, cb) { 10 | cb(null, "public/img/products"); 11 | }, 12 | filename: function (req, file, cb) { 13 | const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9); 14 | cb( 15 | null, 16 | file.fieldname + "-" + uniqueSuffix + path.extname(file.originalname) 17 | ); 18 | }, 19 | }); 20 | 21 | const upload = multer({ storage }); 22 | 23 | /* GET home page. */ 24 | router.get("/", controller.index); 25 | 26 | /* GET Product */ 27 | router.get("/detail/:id", controller.detail); 28 | 29 | //rutas para crear 30 | router.get("/create", controller.create); 31 | router.post("/", upload.single("img"), controller.store); 32 | 33 | //rutas para editar 34 | router.get("/edit/:id", controller.edit); 35 | router.put("/:id", upload.single("img"), controller.update); 36 | 37 | //ruta para eliminar 38 | router.delete("/:id", controller.delete); 39 | 40 | module.exports = router; 41 | -------------------------------------------------------------------------------- /src/database/migrations/20220823165755-UserMigration.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | up: async (queryInterface, Sequelize) => { 5 | await queryInterface.createTable("users", { 6 | id: { 7 | type: Sequelize.INTEGER(11), 8 | primaryKey: true, 9 | autoIncrement: true, 10 | allowNull: false, 11 | }, 12 | name: { 13 | type: Sequelize.STRING(50), 14 | allowNull: false, 15 | }, 16 | 17 | email: { 18 | type: Sequelize.STRING(100), 19 | allowNull: false, 20 | }, 21 | password: { 22 | type: Sequelize.STRING(100), 23 | allowNull: false, 24 | }, 25 | img: { 26 | type: Sequelize.STRING(100), 27 | allowNull: true, 28 | }, 29 | createdAt: { 30 | allowNull: false, 31 | type: Sequelize.DATE, 32 | }, 33 | updatedAt: { 34 | allowNull: false, 35 | type: Sequelize.DATE, 36 | }, 37 | deletedAt: { 38 | allowNull: true, 39 | type: Sequelize.DATE, 40 | }, 41 | }); 42 | }, 43 | 44 | down: async (queryInterface, Sequelize) => { 45 | await queryInterface.dropTable("users"); 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /src/database/migrations/20220823171014-ProductMigration.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | up: async (queryInterface, Sequelize) => { 5 | await queryInterface.createTable("products", { 6 | id: { 7 | type: Sequelize.INTEGER(11), 8 | primaryKey: true, 9 | autoIncrement: true, 10 | allowNull: false, 11 | }, 12 | name: { 13 | type: Sequelize.STRING(100), 14 | allowNull: false, 15 | }, 16 | img: { 17 | type: Sequelize.STRING(100), 18 | allowNull: true, 19 | }, 20 | price: { 21 | type: Sequelize.DECIMAL(10, 2), 22 | allowNull: false, 23 | }, 24 | marked: { 25 | type: Sequelize.BOOLEAN, 26 | defaultValue: false, 27 | }, 28 | createdAt: { 29 | allowNull: false, 30 | type: Sequelize.DATE, 31 | }, 32 | updatedAt: { 33 | allowNull: false, 34 | type: Sequelize.DATE, 35 | }, 36 | deletedAt: { 37 | allowNull: true, 38 | type: Sequelize.DATE, 39 | }, 40 | }); 41 | }, 42 | 43 | down: async (queryInterface, Sequelize) => { 44 | await queryInterface.dropTable("products"); 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /src/middlewares/userValidationsRegister.js: -------------------------------------------------------------------------------- 1 | const { body } = require("express-validator"); 2 | 3 | const validations = [ 4 | body("nombre") 5 | .trim() 6 | .notEmpty() 7 | .withMessage("Debes de ingresar tu nombre") 8 | .bail() 9 | .isLength({ min: 3, max: 20 }) 10 | .withMessage("Debes ingresar entre 3 y 20 caracteres"), 11 | body("email") 12 | .notEmpty() 13 | .withMessage("Debes de ingresar tu email") 14 | .bail() 15 | .isEmail() 16 | .withMessage("Debes ingresar un formato de correo electrónico válido"), 17 | body("password") 18 | .trim() 19 | .notEmpty() 20 | .withMessage("Debes de ingresar una contraseña") 21 | .bail() 22 | .isStrongPassword() 23 | .withMessage( 24 | "Tu contraseña debe tener mínimo 8 caracteres, una mayúscula, una minúscula y un símbolo" 25 | ), 26 | body("confirm-password") 27 | .trim() 28 | .notEmpty() 29 | .withMessage("Debes de ingresar una contraseña") 30 | .bail() 31 | .custom((value, { req }) => { 32 | if (value !== req.body.password) { 33 | // Mensaje en caso de no coincidir 34 | throw new Error("Las contraseñas no coinciden"); 35 | } 36 | return true; 37 | }), 38 | ]; 39 | 40 | module.exports = validations; 41 | -------------------------------------------------------------------------------- /public/js/addons/progressBar.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | $.fn.progressBar = function (givenValue) { 3 | const $this = $(this); 4 | 5 | function init(selector) { 6 | const progressValue = selector.children().attr('aria-valuenow'); 7 | selector.children().width(`${progressValue}%`); 8 | selector.children().html(''); 9 | $this.hasClass('md-progress') ? selector.children().children().addClass('md-progress-bar-text') : selector.children().children().addClass('progress-bar-text'); 10 | (progressValue !== 100) ? selector.children().children().text(`${progressValue}%`) : selector.children().children().html(''); 11 | } 12 | 13 | function set(selector, value) { 14 | selector.children().removeClass('success fail active'); 15 | selector.children().attr('aria-valuenow', value); 16 | init(selector); 17 | if (value > 100) { 18 | return false; 19 | } else if (value === 100) { 20 | selector.children().addClass('success'); 21 | } else if (value < 30) { 22 | selector.children().addClass('fail'); 23 | } else { 24 | selector.children().addClass('active'); 25 | } 26 | return true; 27 | } 28 | 29 | set($this, givenValue); 30 | }; 31 | }(jQuery)); 32 | -------------------------------------------------------------------------------- /src/database/products.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "producto 1 editado", 5 | "price": "201", 6 | "img": "img-1661268122380-595561361.jpg" 7 | }, 8 | { 9 | "id": 2, 10 | "name": "Cambio de nombre", 11 | "price": "200", 12 | "img": "img-1661268130149-305096459.jpg" 13 | }, 14 | { 15 | "id": 3, 16 | "name": "prod 3", 17 | "price": "11", 18 | "img": "img-1661268145315-106564143.jpg" 19 | }, 20 | { 21 | "id": 4, 22 | "name": "producto 4", 23 | "price": "400", 24 | "img": "img-1661268157858-278932779.jpg" 25 | }, 26 | { 27 | "id": 5, 28 | "name": "prodcut con imagen", 29 | "price": "122", 30 | "img": "img-1661268166822-528372102.jpg" 31 | }, 32 | { 33 | "id": 6, 34 | "name": "Administrador", 35 | "price": "21", 36 | "img": "img-1661268174526-879242619.jpg" 37 | }, 38 | { 39 | "id": 7, 40 | "name": "perocha", 41 | "price": "21", 42 | "img": "img-1661268190564-815606767.jpg" 43 | }, 44 | { 45 | "id": 8, 46 | "name": "Otro Mas", 47 | "price": "1343", 48 | "img": "img-1661268339991-990446140.jpg" 49 | } 50 | ] -------------------------------------------------------------------------------- /public/css/addons/zmd.hierarchical-display.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Material Design Hierarchical Display by Sergey Kupletsky (@zavoloklom) - https://github.com/zavoloklom/material-design-hierarchical-display/ 3 | * License - https://github.com/zavoloklom/material-design-hierarchical-display/blob/master/LICENSE (MIT License) 4 | */.zmd-hierarchical-display{visibility:hidden}.zmd-hierarchical-display.in{visibility:visible}.zmd-hierarchical-displaying{visibility:visible}.animation{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.animation.zoomedIn,.animation.zoomedOut{-webkit-animation-timing-function:cubic-bezier(.55,0,.1,1);animation-timing-function:cubic-bezier(.55,0,.1,1)}@-webkit-keyframes zoomedIn{from{-webkit-transform:scale(0);transform:scale(0)}to{-webkit-transform:scale(1);transform:scale(1)}}@keyframes zoomedIn{from{-webkit-transform:scale(0);transform:scale(0)}to{-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframes zoomedOut{from{-webkit-transform:scale(1);transform:scale(1)}to{-webkit-transform:scale(0);transform:scale(0)}}@keyframes zoomedOut{from{-webkit-transform:scale(1);transform:scale(1)}to{-webkit-transform:scale(0);transform:scale(0)}}.zoomedIn{-webkit-animation-name:zoomedIn;animation-name:zoomedIn}.zoomedOut{-webkit-animation-name:zoomedOut;animation-name:zoomedOut} -------------------------------------------------------------------------------- /src/routes/auth.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const path = require("path"); 3 | const router = express.Router(); 4 | const multer = require("multer"); 5 | const { check } = require("express-validator"); 6 | 7 | const controller = require("../controllers/authController"); 8 | const redirectIfAutenticated = require("../middlewares/redirectIfAutenticated"); 9 | const authMiddleware = require("../middlewares/authMiddleware"); 10 | const userValidationsLogin = require("../middlewares/userValidationsLogin"); 11 | 12 | // const storage = multer.diskStorage({ 13 | // destination: function (req, file, cb) { 14 | // cb(null, 'public/img/users') 15 | // }, 16 | // filename: function (req, file, cb) { 17 | // const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9) 18 | // cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname)) 19 | // } 20 | // }) 21 | 22 | // const upload = multer ({storage}) 23 | 24 | router.get("/login", redirectIfAutenticated, controller.showLogin); 25 | router.post("/login", controller.login); 26 | 27 | router.get("/register", redirectIfAutenticated, controller.showRegister); 28 | router.post("/register", userValidationsLogin, controller.register); 29 | 30 | router.post("/logout", controller.logout); 31 | 32 | router.get("/profile", authMiddleware, controller.profile); 33 | 34 | module.exports = router; 35 | -------------------------------------------------------------------------------- /src/database/migrations/20220823171705-OrderMigration.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | up: async (queryInterface, Sequelize) => { 5 | await queryInterface.createTable("orders", { 6 | id: { 7 | type: Sequelize.INTEGER(11), 8 | primaryKey: true, 9 | autoIncrement: true, 10 | allowNull: false, 11 | }, 12 | userId: { 13 | type: Sequelize.INTEGER(11), 14 | allowNull: false, 15 | references: { 16 | model: { 17 | tableName: "users", 18 | }, 19 | key: "id", 20 | }, 21 | }, 22 | total: { 23 | type: Sequelize.DECIMAL(10, 2), 24 | allowNull: false, 25 | }, 26 | paymentMethod: { 27 | type: Sequelize.STRING(25), 28 | allowNull: false, 29 | }, 30 | shippingMethod: { 31 | type: Sequelize.STRING(25), 32 | allowNull: true, 33 | }, 34 | createdAt: { 35 | allowNull: false, 36 | type: Sequelize.DATE, 37 | }, 38 | updatedAt: { 39 | allowNull: false, 40 | type: Sequelize.DATE, 41 | }, 42 | deletedAt: { 43 | allowNull: true, 44 | type: Sequelize.DATE, 45 | }, 46 | }); 47 | }, 48 | 49 | down: async (queryInterface, Sequelize) => { 50 | await queryInterface.dropTable("orders"); 51 | }, 52 | }; 53 | -------------------------------------------------------------------------------- /public/scss/free/_carousels.scss: -------------------------------------------------------------------------------- 1 | // Carousels 2 | .carousel { 3 | .carousel-control-prev-icon, 4 | .carousel-control-next-icon { 5 | width: $carousel-control-icon-width; 6 | height: $carousel-control-icon-height; 7 | } 8 | .carousel-control-prev-icon { 9 | background-image: $carousel-control-prev-icon; 10 | } 11 | .carousel-control-next-icon { 12 | background-image: $carousel-control-next-icon; 13 | } 14 | .carousel-indicators { 15 | li { 16 | width: $carousel-indicators-width; 17 | height: $carousel-indicators-height; 18 | cursor: pointer; 19 | border-radius: $carousel-indicators-border-radius; 20 | } 21 | } 22 | } 23 | .carousel-fade { 24 | .carousel-item { 25 | opacity: 0; 26 | transition-duration: $carousel-transition-duration; 27 | transition-property: opacity; 28 | } 29 | .carousel-item.active, 30 | .carousel-item-next.carousel-item-left, 31 | .carousel-item-prev.carousel-item-right { 32 | opacity: 1; 33 | } 34 | .carousel-item-left, 35 | .carousel-item-right { 36 | &.active { 37 | opacity: 0; 38 | } 39 | } 40 | .carousel-item-next, 41 | .carousel-item-prev, 42 | .carousel-item.active, 43 | .carousel-item-left.active, 44 | .carousel-item-prev.active { 45 | transform: $carousel-item-transform; 46 | @supports (transform-style: preserve-3d) { 47 | transform: $carousel-item-transform-2; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/views/partials/footer.ejs: -------------------------------------------------------------------------------- 1 | 2 | 46 | -------------------------------------------------------------------------------- /public/scss/core/_helpers.scss: -------------------------------------------------------------------------------- 1 | // Helpers 2 | // MDB helpers 3 | .img-fluid, 4 | .video-fluid { 5 | max-width: 100%; 6 | height: auto; 7 | } 8 | 9 | .flex-center { 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | height: 100%; 14 | 15 | p { 16 | margin: 0; 17 | } 18 | 19 | ul { 20 | text-align: center; 21 | 22 | li { 23 | margin-bottom: $flex-center-ul-mb; 24 | 25 | &:last-of-type { 26 | margin-bottom: 0; 27 | } 28 | } 29 | } 30 | } 31 | 32 | .hr-light { 33 | border-top: 1px solid $hr-light; 34 | } 35 | 36 | .hr-dark { 37 | border-top: 1px solid $hr-dark; 38 | } 39 | 40 | // Responsive width 41 | .w-responsive { 42 | width: 75%; 43 | 44 | @media (max-width: 740px) { 45 | width: 100%; 46 | } 47 | } 48 | 49 | // Collapsible body 50 | .collapsible-body { 51 | display: none; 52 | } 53 | 54 | .jumbotron { 55 | background-color: $white-base; 56 | border-radius: $border-radius-base; 57 | box-shadow: $z-depth-1; 58 | } 59 | 60 | @each $name, 61 | $color in $basic-mdb-colors { 62 | @include bg-variant(".bg-#{$name}", $color); 63 | 64 | .border-#{$name} { 65 | border-color: $color !important; 66 | } 67 | } 68 | 69 | .card-img-100 { 70 | width: 100px; 71 | height: 100px; 72 | } 73 | 74 | .card-img-64 { 75 | width: 64px; 76 | height: 64px; 77 | } 78 | 79 | .mml-1 { 80 | margin-left: - .25rem !important; 81 | } 82 | 83 | .flex-1 { 84 | flex: 1; 85 | } 86 | -------------------------------------------------------------------------------- /public/img/lightbox/default-skin.svg: -------------------------------------------------------------------------------- 1 | default-skin 2 -------------------------------------------------------------------------------- /public/scss/core/_masks.scss: -------------------------------------------------------------------------------- 1 | // Masks 2 | // General properties 3 | .view { 4 | position: relative; 5 | overflow: hidden; 6 | cursor: default; 7 | .mask { 8 | position: absolute; 9 | top: 0; 10 | right: 0; 11 | bottom: 0; 12 | left: 0; 13 | width: 100%; 14 | height: 100%; 15 | overflow: hidden; 16 | background-attachment: fixed; 17 | } 18 | img, video { 19 | position: relative; 20 | display: block; 21 | } 22 | video { 23 | &.video-intro { 24 | top: 50%; 25 | left: 50%; 26 | z-index: -100; 27 | width: auto; 28 | min-width: 100%; 29 | height: auto; 30 | min-height: 100%; 31 | transition: $intro-video-transition opacity; 32 | transform: $intro-video-transform; 33 | } 34 | } 35 | } 36 | 37 | // Overlay 38 | .overlay { 39 | .mask { 40 | opacity: 0; 41 | transition: $mask-overlay-transition; 42 | &:hover { 43 | opacity: 1; 44 | } 45 | } 46 | } 47 | 48 | // Zoom 49 | .zoom { 50 | img, video { 51 | transition: $mask-zoom-transition; 52 | } 53 | &:hover { 54 | img, video { 55 | transform: $mask-zoom-transform; 56 | } 57 | } 58 | } 59 | 60 | // Patterns 61 | $patterns: ( 62 | 1: "01", 63 | 2: "02", 64 | 3: "03", 65 | 4: "04", 66 | 5: "05", 67 | 6: "06", 68 | 7: "07", 69 | 8: "08", 70 | 9: "09" 71 | ) !default; 72 | 73 | @each $no, $filename in $patterns { 74 | .pattern-#{$no} { 75 | background: url("#{$image-path}/overlays/#{$filename}.png"); 76 | background-attachment: fixed; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/database/migrations/20220823172558-OrderItemMigration.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | up: async (queryInterface, Sequelize) => { 5 | await queryInterface.createTable("orderitems", { 6 | id: { 7 | type: Sequelize.INTEGER(11), 8 | primaryKey: true, 9 | autoIncrement: true, 10 | allowNull: false, 11 | }, 12 | orderId: { 13 | type: Sequelize.INTEGER(11), 14 | allowNull: false, 15 | references: { 16 | model: { 17 | tableName: "orders", 18 | }, 19 | key: "id", 20 | }, 21 | }, 22 | productId: { 23 | type: Sequelize.INTEGER(11), 24 | allowNull: true, 25 | references: { 26 | model: { 27 | tableName: "products", 28 | }, 29 | key: "id", 30 | }, 31 | }, 32 | name: { 33 | type: Sequelize.STRING(100), 34 | allowNull: false, 35 | }, 36 | price: { 37 | type: Sequelize.DECIMAL(10, 2), 38 | allowNull: false, 39 | }, 40 | quantity: { 41 | type: Sequelize.INTEGER(11), 42 | allowNull: false, 43 | }, 44 | createdAt: { 45 | allowNull: false, 46 | type: Sequelize.DATE, 47 | }, 48 | updatedAt: { 49 | allowNull: false, 50 | type: Sequelize.DATE, 51 | }, 52 | deletedAt: { 53 | allowNull: true, 54 | type: Sequelize.DATE, 55 | }, 56 | }); 57 | }, 58 | 59 | down: async (queryInterface, Sequelize) => { 60 | await queryInterface.dropTable("orderitems"); 61 | }, 62 | }; 63 | -------------------------------------------------------------------------------- /public/scss/free/_tables.scss: -------------------------------------------------------------------------------- 1 | // Tables 2 | table { 3 | th { 4 | font-size: $table-th-font-size; 5 | font-weight: 400; 6 | } 7 | td { 8 | font-size: $table-td-font-size; 9 | font-weight: 300; 10 | } 11 | &.table { 12 | thead th { 13 | border-top: none; 14 | } 15 | th, 16 | td { 17 | padding-top: $table-th-padding-top; 18 | padding-bottom: $table-td-padding-bottom; 19 | } 20 | a { 21 | margin: 0; 22 | color: $table-a-color; 23 | // &.btn { 24 | // color: inherit; 25 | // } 26 | } 27 | .label-table { 28 | height: $table-label-line-height; 29 | padding: 0; 30 | margin: 0; 31 | line-height: $table-label-height; 32 | } 33 | &.btn-table { 34 | td { 35 | vertical-align: middle; 36 | } 37 | } 38 | } 39 | &.table-hover { 40 | tbody { 41 | tr { 42 | &:hover { 43 | background-color: $table-hover-background-color; 44 | transition: $table-hover-transition; 45 | } 46 | } 47 | } 48 | } 49 | .th-lg { 50 | min-width: $table-th-lg-min-width; 51 | } 52 | .th-sm { 53 | min-width: $table-th-sm-min-width; 54 | } 55 | &.table-sm { 56 | th, 57 | td { 58 | padding-top: $table-sm-padding-y; 59 | padding-bottom: $table-sm-padding-y; 60 | } 61 | } 62 | } 63 | .table-scroll-vertical { 64 | max-height: $table-scroll-vertical-max-height; 65 | overflow-y: auto; 66 | } 67 | .table-fixed { 68 | table-layout: fixed; 69 | } 70 | .table-responsive, 71 | .table-responsive-sm, 72 | .table-responsive-md, 73 | .table-responsive-lg, 74 | .table-responsive-xl { 75 | > .table-bordered { 76 | border-top: 1px solid #dee2e6; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /public/scss/mdb.lite.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Material Design for Bootstrap 4 3 | * Version: MDB Lite 4.8.11 4 | * 5 | * 6 | * Copyright: Material Design for Bootstrap 7 | * https://mdbootstrap.com/ 8 | * 9 | * Read the license: https://mdbootstrap.com/general/license/ 10 | * 11 | * 12 | * Documentation: https://mdbootstrap.com/ 13 | * 14 | * Getting started: https://mdbootstrap.com/docs/jquery/getting-started/download/ 15 | * 16 | * Tutorials: https://mdbootstrap.com/education/bootstrap/ 17 | * 18 | * Templates: https://mdbootstrap.com/templates/ 19 | * 20 | * Support: https://mdbootstrap.com/support/ 21 | * 22 | * Contact: office@mdbootstrap.com 23 | * 24 | * Attribution: Animate CSS, Twitter Bootstrap, Materialize CSS, Normalize CSS, Waves JS, WOW JS, Toastr, Chart.js 25 | * 26 | */ 27 | 28 | @charset "UTF-8"; 29 | 30 | // Bootstrap 31 | @import "core/bootstrap/functions"; 32 | @import "core/bootstrap/variables"; 33 | 34 | // CORE 35 | @import "core/mixins"; 36 | // Your custom variables 37 | @import "custom-variables"; 38 | @import "core/colors"; 39 | @import "core/variables"; 40 | @import "core/global"; 41 | @import "core/helpers"; 42 | @import "core/typography"; 43 | @import "core/masks"; 44 | @import "core/waves"; 45 | 46 | // FREE 47 | @import "free/animations-basic"; 48 | @import "free/buttons"; 49 | @import "free/cards"; 50 | @import "free/dropdowns"; 51 | @import "free/input-group"; 52 | @import "free/navbars"; 53 | @import "free/pagination"; 54 | @import "free/badges"; 55 | @import "free/modals"; 56 | @import "free/carousels"; 57 | @import "free/forms"; 58 | @import "free/msc"; 59 | @import "free/footers"; 60 | @import "free/list-group"; 61 | @import "free/tables"; 62 | @import "free/depreciated"; 63 | @import "free/steppers"; 64 | @import "free/loader"; 65 | @import "free/treeview"; 66 | 67 | // Your custom styles 68 | @import "custom-styles"; 69 | -------------------------------------------------------------------------------- /public/css/addons/zmd.hierarchical-display.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Material Design Hierarchical Display by Sergey Kupletsky (@zavoloklom) - https://github.com/zavoloklom/material-design-hierarchical-display/ 3 | * License - https://github.com/zavoloklom/material-design-hierarchical-display/blob/master/LICENSE (MIT License) 4 | */ 5 | .zmd-hierarchical-display { 6 | visibility: hidden; } 7 | .zmd-hierarchical-display.in { 8 | visibility: visible; } 9 | 10 | .zmd-hierarchical-displaying { 11 | visibility: visible; } 12 | 13 | .animation { 14 | -webkit-animation-duration: 1s; 15 | animation-duration: 1s; 16 | -webkit-animation-fill-mode: both; 17 | animation-fill-mode: both; } 18 | 19 | .animation.zoomedIn, 20 | .animation.zoomedOut { 21 | -webkit-animation-timing-function: cubic-bezier(0.55, 0, 0.1, 1); 22 | animation-timing-function: cubic-bezier(0.55, 0, 0.1, 1); } 23 | 24 | @-webkit-keyframes zoomedIn { 25 | from { 26 | -webkit-transform: scale(0); 27 | transform: scale(0); } 28 | to { 29 | -webkit-transform: scale(1); 30 | transform: scale(1); } } 31 | 32 | @keyframes zoomedIn { 33 | from { 34 | -webkit-transform: scale(0); 35 | transform: scale(0); } 36 | to { 37 | -webkit-transform: scale(1); 38 | transform: scale(1); } } 39 | 40 | @-webkit-keyframes zoomedOut { 41 | from { 42 | -webkit-transform: scale(1); 43 | transform: scale(1); } 44 | to { 45 | -webkit-transform: scale(0); 46 | transform: scale(0); } } 47 | 48 | @keyframes zoomedOut { 49 | from { 50 | -webkit-transform: scale(1); 51 | transform: scale(1); } 52 | to { 53 | -webkit-transform: scale(0); 54 | transform: scale(0); } } 55 | 56 | .zoomedIn { 57 | -webkit-animation-name: zoomedIn; 58 | animation-name: zoomedIn; } 59 | 60 | .zoomedOut { 61 | -webkit-animation-name: zoomedOut; 62 | animation-name: zoomedOut; } 63 | -------------------------------------------------------------------------------- /src/views/auth/login.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- include('../partials/head') %> 6 | 7 | 8 | 9 | 10 | 11 | <%- include('../partials/header') %> 12 |
13 |
14 |
15 |

Ingresar

16 |
17 | 19 | 20 | <% if (locals.errors && errors.email) { %> 21 | 22 | <%= errors.email.msg %> 23 | 24 | <% } %> 25 |
26 |
27 | 28 | 29 | <% if (locals.errors && errors.password) { %> 30 | 31 | <%= errors.password.msg %> 32 | 33 | <% } %> 34 |
35 |
36 | 37 | 38 |
39 |
40 | 41 |
42 |
43 |
44 | <%- include('../partials/footer') %> 45 | <%- include('../partials/scripts') %> 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/controllers/productController.js: -------------------------------------------------------------------------------- 1 | const db = require("../database/models"); 2 | 3 | const controller = { 4 | index: async function (req, res) { 5 | let products = await db.Product.findAll(); 6 | return res.render("products/list", { products }); 7 | }, 8 | detail: async function (req, res) { 9 | let product = db.Product.findByPk(req.params.id); 10 | return res.render("products/detail", { product }); 11 | }, 12 | create: function (req, res) { 13 | return res.render("products/create"); 14 | }, 15 | store: async function (req, res) { 16 | let image = ""; 17 | if (req.file) { 18 | image = req.file.filename; 19 | } 20 | 21 | //guardo el nuevo producto con la estructura 22 | await db.Product.create({ 23 | name: req.body.name, 24 | price: req.body.price, 25 | img: image, 26 | marked: req.body.marked ? true : false, 27 | }); 28 | 29 | //redireccione al listado de productos 30 | return res.redirect("/products"); 31 | }, 32 | edit: async function (req, res) { 33 | let product = await db.Product.findByPk(req.params.id); 34 | if (product) { 35 | return res.render("products/edit", { product }); 36 | } 37 | return res.redirect("/products"); 38 | }, 39 | update: async function (req, res) { 40 | let product = await db.Product.findByPk(req.params.id); 41 | if (product) { 42 | let image = product.img; 43 | if (req.file) { 44 | image = req.file.filename; 45 | } 46 | await product.update({ 47 | name: req.body.name, 48 | price: req.body.price, 49 | img: image, 50 | marked: req.body.marked ? true : false, 51 | }); 52 | } 53 | return res.redirect("/products"); 54 | }, 55 | delete: async function (req, res) { 56 | await db.Product.destroy({ 57 | where: { id: req.params.id }, 58 | }); 59 | 60 | res.redirect("/products"); 61 | }, 62 | }; 63 | 64 | module.exports = controller; 65 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../src/app'); 8 | var debug = require('debug')('magazine:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /src/views/products/create.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- include('../partials/head') %> 6 | 7 | 8 | 9 | 10 | 11 | <%- include('../partials/header') %> 12 |
13 |
14 |

Nuevo Producto

15 |
16 |
17 |
18 |
19 |
20 | 21 | 22 |
23 |
24 | 25 | 26 |
27 |
28 | 30 | 31 |
32 |
33 | 34 | 35 |
36 | 37 |
38 |
39 |
40 |
41 |
42 |
43 | <%- include('../partials/footer') %> 44 | <%- include('../partials/scripts') %> 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/views/partials/product.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 |
7 |
8 |
9 |
10 |
11 | 12 | 13 | 14 | <%= prod.name %> 15 | <% if (prod.marked) { %> 16 | Novedad 17 | <% } %> 18 | 19 | 20 |
21 |

22 | $<%= prod.price %> 23 |

24 |
25 | 26 |
27 | <% if (locals.userFound && locals.userAdmin) { %> 28 |
29 | 30 | 31 | 32 |
33 | 36 |
37 |
38 | <% } %> 39 |
40 | 41 |
42 | 43 |
-------------------------------------------------------------------------------- /public/scss/mdb.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Material Design for Bootstrap 4 3 | * Version: MDB FREE 4.8.11 4 | * 5 | * 6 | * Copyright: Material Design for Bootstrap 7 | * https://mdbootstrap.com/ 8 | * 9 | * Read the license: https://mdbootstrap.com/general/license/ 10 | * 11 | * 12 | * Documentation: https://mdbootstrap.com/ 13 | * 14 | * Getting started: https://mdbootstrap.com/docs/jquery/getting-started/download/ 15 | * 16 | * Tutorials: https://mdbootstrap.com/education/bootstrap/ 17 | * 18 | * Templates: https://mdbootstrap.com/templates/ 19 | * 20 | * Support: https://mdbootstrap.com/support/ 21 | * 22 | * Contact: office@mdbootstrap.com 23 | * 24 | * Attribution: Animate CSS, Twitter Bootstrap, Materialize CSS, Normalize CSS, Waves JS, WOW JS, Toastr, Chart.js 25 | * 26 | */ 27 | 28 | @charset "UTF-8"; 29 | 30 | // Bootstrap 31 | @import "core/bootstrap/functions"; 32 | @import "core/bootstrap/variables"; 33 | @import "core/bootstrap/rfs"; 34 | 35 | // CORE 36 | @import "core/mixins"; 37 | // Your custom variables 38 | @import "custom-variables"; 39 | @import "core/colors"; 40 | @import "core/variables"; 41 | @import "core/global"; 42 | @import "core/helpers"; 43 | @import "core/typography"; 44 | @import "core/masks"; 45 | @import "core/waves"; 46 | 47 | // FREE 48 | @import "free/animations-basic"; 49 | @import "free/modules/animations-extended/module"; 50 | @import "free/buttons"; 51 | @import "free/cards"; 52 | @import "free/dropdowns"; 53 | @import "free/input-group"; 54 | @import "free/navbars"; 55 | @import "free/pagination"; 56 | @import "free/badges"; 57 | @import "free/modals"; 58 | @import "free/carousels"; 59 | @import "free/forms"; 60 | @import "free/msc"; 61 | @import "free/footers"; 62 | @import "free/list-group"; 63 | @import "free/tables"; 64 | @import "free/depreciated"; 65 | @import "free/steppers"; 66 | @import "free/loader"; 67 | @import "free/treeview"; 68 | // Free addons 69 | // @import "addons/datatables"; 70 | // @import "addons/datatables-select"; 71 | // @import "addons/directives"; 72 | // @import "addons/hierarchical-display"; 73 | // @import "addons/flags"; 74 | // @import "addons/rating"; 75 | 76 | // Your custom styles 77 | @import "custom-styles"; 78 | -------------------------------------------------------------------------------- /src/views/products/edit.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- include('../partials/head') %> 6 | 7 | 8 | 9 | 10 | 11 | <%- include('../partials/header') %> 12 |
13 |
14 |

Editar Producto

15 |
16 |
17 |
18 |
20 |
21 | 22 | 23 |
24 | 25 |
26 | 28 | 29 |
30 |
31 | 33 | 34 |
35 |
36 | > 38 | 39 |
40 | 41 | 42 |
43 |
44 |
45 |
46 | 47 |
48 |
49 | <%- include('../partials/footer') %> 50 | <%- include('../partials/scripts') %> 51 | 52 | 53 | -------------------------------------------------------------------------------- /public/scss/free/_pagination.scss: -------------------------------------------------------------------------------- 1 | // Pagination 2 | .pagination { 3 | .page-item { 4 | &.active { 5 | .page-link { 6 | color: $white-base; 7 | background-color: $primary-color; 8 | border-radius: $border-radius-base; 9 | box-shadow: $z-depth-1; 10 | transition: $pagination-active-transition; 11 | &:hover { 12 | background-color: $primary-color; 13 | } 14 | } 15 | } 16 | &.disabled { 17 | .page-link { 18 | color: $pagination-page-item-disabled-color; 19 | } 20 | } 21 | .page-link { 22 | font-size: $pagination-page-link-font-size; 23 | color: $pagination-page-link-color; 24 | background-color: transparent; 25 | border: 0; 26 | outline: 0; 27 | transition: $pagination-page-link-transition; 28 | &:hover { 29 | background-color: $pagination-page-link-hover-bg-color; 30 | border-radius: $border-radius-base; 31 | transition: $pagination-page-link-transition; 32 | } 33 | &:focus { 34 | background-color: transparent; 35 | box-shadow: none; 36 | } 37 | } 38 | } 39 | &.pagination-lg { 40 | .page-item { 41 | .page-link { 42 | font-size: $pagination-page-link-font-size-lg; 43 | } 44 | } 45 | } 46 | &.pagination-sm { 47 | .page-item { 48 | .page-link { 49 | font-size: $pagination-page-link-font-size-sm; 50 | } 51 | } 52 | } 53 | &.pagination-circle { 54 | .page-item { 55 | .page-link { 56 | margin-right: $pagination-circle-margin-x; 57 | margin-left: $pagination-circle-margin-x; 58 | border-radius: $pagination-circle-border-radius; 59 | &:hover { 60 | border-radius: $pagination-circle-border-radius; 61 | } 62 | } 63 | &.active { 64 | .page-link { 65 | border-radius: $pagination-circle-border-radius; 66 | } 67 | } 68 | } 69 | } 70 | @each $name, $color in $pagination-colors { 71 | &.pg-#{$name} { 72 | .page-item { 73 | &.active { 74 | .page-link { 75 | background-color: $color; 76 | &:hover { 77 | background-color: $color; 78 | } 79 | } 80 | } 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /public/css/general.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | text-decoration: none; 6 | font-family: 'Kdam Thmor Pro', sans-serif; 7 | } 8 | 9 | body { 10 | font-size: 14px; 11 | background-color:#fff; 12 | } 13 | 14 | header{ 15 | position: fixed; 16 | top: 0; 17 | right: 0; 18 | left: 0; 19 | background-color: antiquewhite; 20 | z-index: 10; 21 | } 22 | .menu-header { 23 | display: flex; 24 | justify-content: space-between; 25 | max-width: 1200px; 26 | margin-left: auto; 27 | margin-right: auto; 28 | } 29 | .logo { 30 | font-size: 38px; 31 | font-weight: bolder; 32 | padding: 2px; 33 | } 34 | 35 | .menu-user { 36 | display: flex; 37 | } 38 | 39 | .menu-user-item { 40 | display: block; 41 | padding: 10px; 42 | background-color: antiquewhite; 43 | text-decoration: none; 44 | color: teal; 45 | } 46 | 47 | .menu-user-item:hover { 48 | background-color: teal; 49 | color: antiquewhite; 50 | } 51 | 52 | main{ 53 | } 54 | 55 | section{ 56 | padding: 20px; 57 | } 58 | 59 | .container{ 60 | margin: 63px 5px 0 5px; 61 | max-width: 1200px; 62 | } 63 | 64 | .main-full{ 65 | height: 91vh; 66 | background-color: burlywood; 67 | } 68 | 69 | .section-title{ 70 | display: flex; 71 | justify-content: space-between; 72 | } 73 | 74 | .btn-new-title{ 75 | display: inline-block; 76 | margin-bottom: 5px; 77 | padding: 10px; 78 | border-radius: 5px; 79 | background-color: rgb(24, 24, 224); 80 | color: aliceblue; 81 | } 82 | .btn-new-title:hover{ 83 | background-color: rgb(68, 68, 255); 84 | } 85 | 86 | .form-new { 87 | padding: 10px; 88 | display: flex; 89 | gap: 10px; 90 | flex-wrap: wrap; 91 | } 92 | 93 | .form-new>div { 94 | flex-basis: 100%; 95 | margin-top: 10px; 96 | } 97 | 98 | .form-new>div>label { 99 | display: block; 100 | } 101 | 102 | .form-new>div>input { 103 | display: block; 104 | width: 100%; 105 | padding: 3px; 106 | border-radius: 3px; 107 | } 108 | 109 | @media (min-width:600px){ 110 | .form-new>div { 111 | flex-basis: 49%; 112 | } 113 | 114 | } 115 | 116 | @media (min-width:1200px) { 117 | .container { 118 | margin-left: auto; 119 | margin-right: auto; 120 | } 121 | } -------------------------------------------------------------------------------- /src/views/auth/register.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- include('../partials/head') %> 6 | 7 | 8 | 9 | 10 | 11 | <%- include('../partials/header') %> 12 | <%- include('../partials/header') %> 13 |
14 |
15 |
16 |

Registrarse

17 |
18 | 20 | 21 | <% if (locals.errors && errors.name) { %> 22 | 23 | <%= errors.name.msg %> 24 | 25 | <% } %> 26 |
27 |
28 | 30 | 31 | <% if (locals.errors && errors.email) { %> 32 | 33 | <%= errors.email.msg %> 34 | 35 | <% } %> 36 |
37 |
38 | 39 | 40 | <% if (locals.errors && errors.password) { %> 41 | 42 | <%= errors.password.msg %> 43 | 44 | <% } %> 45 |
46 |
47 | 48 | 49 |
50 | 51 |
52 | 53 | 54 |
55 |
56 |
57 | <%- include('../partials/footer') %> 58 | <%- include('../partials/scripts') %> 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | const createError = require("http-errors"); 2 | const express = require("express"); 3 | const path = require("path"); 4 | const cookieParser = require("cookie-parser"); 5 | const logger = require("morgan"); 6 | const mo = require("method-override"); 7 | const ses = require("express-session"); 8 | 9 | const indexRouter = require("./routes/index"); 10 | const usersRouter = require("./routes/users"); 11 | const productsRouter = require("./routes/products"); 12 | const authRouter = require("./routes/auth"); 13 | const apiRouter = require("./routes/api"); 14 | const sessionMiddleware = require("./middlewares/sessionMiddleware"); 15 | const sessionTimeMiddleware = require("./middlewares/sessionTimeMiddleware"); 16 | const menuMiddleware = require("./middlewares/menuMiddleware"); 17 | 18 | const app = express(); 19 | 20 | // .ENV 21 | require("dotenv").config(); 22 | 23 | // view engine setup 24 | app.set("views", path.join(__dirname, "views")); 25 | app.set("view engine", "ejs"); 26 | 27 | app.use(logger("dev")); 28 | app.use(express.json()); 29 | app.use(express.urlencoded({ extended: false })); 30 | app.use(mo("_method")); 31 | app.use(cookieParser()); 32 | app.use(express.static(path.join(__dirname, "../public"))); 33 | app.use( 34 | ses({ secret: "es un secreto", resave: false, saveUninitialized: true }) 35 | ); 36 | 37 | // esto es un middleware de tiempo de session 38 | app.use(sessionMiddleware); 39 | app.use(sessionTimeMiddleware); 40 | 41 | app.use(menuMiddleware); 42 | 43 | app.use("/", indexRouter); 44 | app.use("/", authRouter); 45 | app.use("/users/", usersRouter); 46 | app.use("/products/", productsRouter); 47 | app.use("/api/", apiRouter); 48 | 49 | // catch 404 and forward to error handler 50 | app.use(function (req, res, next) { 51 | return res.render("errors/404"); 52 | //next(createError(404)); 53 | }); 54 | 55 | // vista no encontrada 56 | app.use(function (err, req, res, next) { 57 | console.log(err); 58 | if (err["view"] != null) { 59 | console.error("errorView", err.message); 60 | return res.render("errors/500"); 61 | } 62 | return next(); 63 | }); 64 | 65 | // error handler 66 | app.use(function (err, req, res, next) { 67 | console.log("errorHandler", err.message); 68 | // set locals, only providing error in development 69 | res.locals.message = err.message; 70 | res.locals.error = req.app.get("env") === "development" ? err : {}; 71 | 72 | // render the error page 73 | res.status(err.status || 500); 74 | res.render("error"); 75 | }); 76 | 77 | module.exports = app; 78 | -------------------------------------------------------------------------------- /public/js/addons/rating.min.js: -------------------------------------------------------------------------------- 1 | (function($){$.fn.mdbRate=function(){var $stars;var myDefaultWhiteList=$.fn.tooltip.Constructor.Default.whiteList 2 | myDefaultWhiteList.textarea=[];myDefaultWhiteList.button=[];var $container=$(this);var titles=['Very bad','Poor','OK','Good','Excellent'];for(var i=0;i<5;i++){$container.append(``)} 4 | $stars=$container.children();if($container.hasClass('rating-faces')){$stars.addClass('far fa-meh-blank')}else if($container.hasClass('empty-stars')){$stars.addClass('far fa-star')}else{$stars.addClass('fas fa-star')} 5 | $stars.on('mouseover',function(){var index=$(this).attr('data-index');markStarsAsActive(index)});function markStarsAsActive(index){unmarkActive();for(var i=0;i<=index;i++){if($container.hasClass('rating-faces')){$($stars.get(i)).removeClass('fa-meh-blank');$($stars.get(i)).addClass('live');switch(index){case '0':$($stars.get(i)).addClass('fa-angry');break;case '1':$($stars.get(i)).addClass('fa-frown');break;case '2':$($stars.get(i)).addClass('fa-meh');break;case '3':$($stars.get(i)).addClass('fa-smile');break;case '4':$($stars.get(i)).addClass('fa-laugh');break}}else if($container.hasClass('empty-stars')){$($stars.get(i)).addClass('fas');switch(index){case '0':$($stars.get(i)).addClass('oneStar');break;case '1':$($stars.get(i)).addClass('twoStars');break;case '2':$($stars.get(i)).addClass('threeStars');break;case '3':$($stars.get(i)).addClass('fourStars');break;case '4':$($stars.get(i)).addClass('fiveStars');break}}else{$($stars.get(i)).addClass('amber-text')}}} 6 | function unmarkActive(){$stars.parent().hasClass('rating-faces')?$stars.addClass('fa-meh-blank'):$stars;$container.hasClass('empty-stars')?$stars.removeClass('fas'):$container;$stars.removeClass('fa-angry fa-frown fa-meh fa-smile fa-laugh live oneStar twoStars threeStars fourStars fiveStars amber-text')} 7 | $stars.on('click',function(){$stars.popover('hide')});$container.on('click','#voteSubmitButton',function(){$stars.popover('hide')});$container.on('click','#closePopoverButton',function(){$stars.popover('hide')});if($container.hasClass('feedback')){$(function(){$stars.popover({container:$container,content:`
`})})} 8 | $stars.tooltip()}})(jQuery) -------------------------------------------------------------------------------- /public/js/modules/treeview.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | (function ($) { 4 | $.fn.mdbTreeview = function () { 5 | var $this = $(this); 6 | 7 | if ($this.hasClass('treeview')) { 8 | var $toggler = $this.find('.rotate'); 9 | $.each($toggler, function (e) { 10 | $($toggler[e]).off('click'); 11 | $($toggler[e]).on('click', function () { 12 | var $this = $(this); 13 | $this.siblings('.nested').toggleClass('active'); 14 | $this.toggleClass('down'); 15 | }); 16 | }); 17 | } 18 | 19 | if ($this.hasClass('treeview-animated')) { 20 | var $elements = $this.find('.treeview-animated-element'); 21 | var $closed = $this.find('.closed'); 22 | $this.find('.nested').hide(); 23 | $closed.off('click'); 24 | $closed.on('click', function () { 25 | var $this = $(this); 26 | var $target = $this.siblings('.nested'); 27 | var $pointer = $this.children('.fa-angle-right'); 28 | $this.toggleClass('open'); 29 | $pointer.toggleClass('down'); 30 | !$target.hasClass('active') ? $target.addClass('active').slideDown() : $target.removeClass('active').slideUp(); 31 | return false; 32 | }); 33 | $elements.off('click'); 34 | $elements.on('click', function () { 35 | var $this = $(this); 36 | $this.hasClass('opened') ? $this.removeClass('opened') : ($elements.removeClass('opened'), $this.addClass('opened')); 37 | }); 38 | } 39 | 40 | if ($this.hasClass('treeview-colorful')) { 41 | var _$elements = $this.find('.treeview-colorful-element'); 42 | 43 | var $header = $this.find('.treeview-colorful-items-header'); 44 | $this.find('.nested').hide(); 45 | $header.off('click'); 46 | $header.on('click', function () { 47 | var $this = $(this); 48 | var $target = $this.siblings('.nested'); 49 | var $pointerPlus = $this.children('.fa-plus-circle'); 50 | var $pointerMinus = $this.children('.fa-minus-circle'); 51 | $this.toggleClass('open'); 52 | $pointerPlus.removeClass('fa-plus-circle'); 53 | $pointerPlus.addClass('fa-minus-circle'); 54 | $pointerMinus.removeClass('fa-minus-circle'); 55 | $pointerMinus.addClass('fa-plus-circle'); 56 | !$target.hasClass('active') ? $target.addClass('active').slideDown() : $target.removeClass('active').slideUp(); 57 | }); 58 | 59 | _$elements.off('click'); 60 | 61 | _$elements.on('click', function () { 62 | var $this = $(this); 63 | $this.hasClass('opened') ? _$elements.removeClass('opened') : (_$elements.removeClass('opened'), $this.addClass('opened')); 64 | }); 65 | } 66 | }; 67 | })(jQuery); -------------------------------------------------------------------------------- /public/scss/free/_buttons.scss: -------------------------------------------------------------------------------- 1 | // Buttons 2 | .btn { 3 | margin: $btn-margin-basic; 4 | color: inherit; 5 | text-transform: uppercase; 6 | word-wrap: break-word; 7 | white-space: normal; 8 | cursor: pointer; 9 | border: 0; 10 | border-radius: $border-radius-base; 11 | box-shadow: $z-depth-1; 12 | transition: $btn-transition; 13 | @include button-size($btn-padding-y-basic, $btn-padding-x-basic, $btn-font-size-basic); 14 | 15 | @include hover-focus-active { 16 | outline: 0; 17 | box-shadow: $z-depth-1-half; 18 | } 19 | 20 | &.btn-block { 21 | margin: inherit; 22 | } 23 | 24 | .fas, 25 | .fab, 26 | .far { 27 | &.right { 28 | margin-left: $btn-icon-margin; 29 | } 30 | &.left { 31 | margin-right: $btn-icon-margin; 32 | } 33 | } 34 | 35 | &.btn-lg { 36 | @include button-size($btn-padding-y-large, $btn-padding-x-large, $btn-font-size-large); 37 | } 38 | &.btn-md { 39 | @include button-size($btn-padding-y-medium, $btn-padding-x-medium, $btn-font-size-medium); 40 | } 41 | &.btn-sm { 42 | @include button-size($btn-padding-y-small, $btn-padding-x-small, $btn-font-size-small); 43 | } 44 | 45 | &.disabled, 46 | &:disabled { 47 | @include hover-focus-active { 48 | box-shadow: $z-depth-1; 49 | } 50 | } 51 | 52 | &[class*="btn-outline-"] { 53 | padding-top: $btn-outline-padding-y-basic; 54 | padding-bottom: $btn-outline-padding-y-basic; 55 | &.btn-lg { 56 | padding-top: $btn-outline-padding-y-large; 57 | padding-bottom: $btn-outline-padding-y-large; 58 | } 59 | &.btn-md { 60 | padding-top: $btn-outline-padding-y-medium; 61 | padding-bottom: $btn-outline-padding-y-medium; 62 | } 63 | &.btn-sm { 64 | padding-top: $btn-outline-padding-y-small; 65 | padding-bottom: $btn-outline-padding-y-small; 66 | } 67 | } 68 | } 69 | 70 | .btn-link { 71 | color: $black-base; 72 | background-color: transparent; 73 | box-shadow: none; 74 | @include hover-focus-active { 75 | color: $black-base; 76 | background-color: transparent; 77 | box-shadow: none; 78 | } 79 | } 80 | 81 | .btn-group { 82 | > .btn:not(:first-child), 83 | > .btn-group:not(:first-child) { 84 | margin-left: -$btn-group-margin; 85 | } 86 | } 87 | 88 | @each $btn_name, $color_value in $mdb-colors { 89 | @include make-button($btn_name, $color_value); 90 | @include make-outline-button($btn_name, $color_value); 91 | } 92 | 93 | @each $name, $val in $gradients { 94 | @include make-gradient-button($name, $val); 95 | } 96 | 97 | .btn-warning:not(:disabled):not(.disabled).active, 98 | .btn-warning:not(:disabled):not(.disabled):active, 99 | .show > .btn-warning.dropdown-toggle { 100 | color: $white-base; 101 | } 102 | 103 | -------------------------------------------------------------------------------- /src/views/order.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- include('./partials/head') %> 6 | 7 | 8 | 9 | <%- include('./partials/header') %> 10 |
11 |
12 |

Pedido Nro

13 |
14 |
15 | 16 | 17 | 18 | 21 | 24 | 27 | 30 | 33 | 34 | 35 | 36 | 37 | 38 |
19 | # 20 | 22 | Producto 23 | 25 | Precio 26 | 28 | Cantidad 29 | 31 | Subtotal 32 |
39 | 42 |
43 |
44 |
45 |
46 | Detalles de la compra 47 |
48 |
49 |
50 |
51 |

Tipo Envio: Correo Argentino

52 |
53 |
54 |

Pago: Efectivo

55 |
56 |
57 |
58 |
59 |
60 |
61 | <%- include('./partials/footer') %> 62 | <%- include('./partials/scripts') %> 63 | 64 | 65 | -------------------------------------------------------------------------------- /public/scss/free/_navbars.scss: -------------------------------------------------------------------------------- 1 | // Navbars 2 | .navbar { 3 | font-weight: $navbar-font-weight; 4 | box-shadow: $z-depth-1; 5 | form { 6 | .md-form { 7 | input { 8 | margin: 0 $navbar-form-input-mr $navbar-form-input-mb $navbar-form-input-ml; 9 | } 10 | } 11 | } 12 | .breadcrumb { 13 | padding: $navbar-breadcrumb-padding-top 0 0 $navbar-breadcrumb-padding-left; 14 | margin: 0; 15 | font-size: $navbar-double-font-size; 16 | font-weight: $navbar-font-weight; 17 | background-color: inherit; 18 | .breadcrumb-item { 19 | color: $white-base; 20 | &.active { 21 | color: $navbar-breadcrumb-color; 22 | } 23 | &:before { 24 | color: $navbar-breadcrumb-color; 25 | } 26 | } 27 | } 28 | .navbar-toggler { 29 | border-width: 0; 30 | outline: 0; 31 | } 32 | .nav-flex-icons { 33 | flex-direction: row; 34 | } 35 | .container { 36 | @media (max-width: $medium-screen) { 37 | width: 100%; 38 | .navbar-toggler-right { 39 | right: 0; 40 | } 41 | } 42 | } 43 | .nav-item { 44 | .nav-link { 45 | display: block; 46 | &.disabled { 47 | &:active { 48 | pointer-events: none; 49 | } 50 | } 51 | .fas, .fab, .far { 52 | padding-right: $navbar-flex-icons-padding-lg; 53 | padding-left: $navbar-flex-icons-padding-lg; 54 | } 55 | @media (max-width: $medium-screen) { 56 | padding-right: $navbar-flex-icons-padding-md; 57 | padding-left: $navbar-flex-icons-padding-md; 58 | } 59 | } 60 | } 61 | .dropdown-menu { 62 | position: absolute !important; 63 | margin-top: 0; 64 | a { 65 | padding: $navbar-dropdown-menu-padding; 66 | font-size: $navbar-dropdown-font-size; 67 | font-weight: $navbar-font-weight; 68 | color: $black; 69 | } 70 | form { 71 | @media (max-width: $small-screen) { 72 | width: 17rem; 73 | } 74 | @media (min-width: $small-screen) { 75 | width: 22rem; 76 | } 77 | } 78 | } 79 | &.navbar-light { 80 | @include make-navbar($navbar-light-disabled-color, $navbar-light-toggler-icon, $black, $navbar-light-hover-color, $navbar-light-bg-active-color); 81 | } 82 | &.navbar-dark { 83 | @include make-navbar($navbar-dark-disabled-color, $navbar-dark-toggler-icon, $white, $navbar-dark-hover-color, $navbar-dark-bg-active-color); 84 | } 85 | &.scrolling-navbar { 86 | @media (min-width: $small-screen) { 87 | padding-top: $navbar-scrolling-padding; 88 | padding-bottom: $navbar-scrolling-padding; 89 | transition: $navbar-scrolling-transition; 90 | .navbar-nav > li { 91 | transition-duration: $navbar-scrolling-transition-duration; 92 | } 93 | &.top-nav-collapse { 94 | padding-top: $navbar-top-collapse-padding; 95 | padding-bottom: $navbar-top-collapse-padding; 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /public/scss/core/bootstrap/_functions.scss: -------------------------------------------------------------------------------- 1 | // Bootstrap functions 2 | // 3 | // Utility mixins and functions for evaluating source code across our variables, maps, and mixins. 4 | 5 | // Ascending 6 | // Used to evaluate Sass maps like our grid breakpoints. 7 | @mixin _assert-ascending($map, $map-name) { 8 | $prev-key: null; 9 | $prev-num: null; 10 | @each $key, $num in $map { 11 | @if $prev-num == null or unit($num) == "%" { 12 | // Do nothing 13 | } @else if not comparable($prev-num, $num) { 14 | @warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !"; 15 | } @else if $prev-num >= $num { 16 | @warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !"; 17 | } 18 | $prev-key: $key; 19 | $prev-num: $num; 20 | } 21 | } 22 | 23 | // Starts at zero 24 | // Used to ensure the min-width of the lowest breakpoint starts at 0. 25 | @mixin _assert-starts-at-zero($map, $map-name: "$grid-breakpoints") { 26 | $values: map-values($map); 27 | $first-value: nth($values, 1); 28 | @if $first-value != 0 { 29 | @warn "First breakpoint in #{$map-name} must start at 0, but starts at #{$first-value}."; 30 | } 31 | } 32 | 33 | // Replace `$search` with `$replace` in `$string` 34 | // Used on our SVG icon backgrounds for custom forms. 35 | // 36 | // @author Hugo Giraudel 37 | // @param {String} $string - Initial string 38 | // @param {String} $search - Substring to replace 39 | // @param {String} $replace ('') - New value 40 | // @return {String} - Updated string 41 | @function str-replace($string, $search, $replace: "") { 42 | $index: str-index($string, $search); 43 | 44 | @if $index { 45 | @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); 46 | } 47 | 48 | @return $string; 49 | } 50 | 51 | // Color contrast 52 | @function color-yiq($color, $dark: $yiq-text-dark, $light: $yiq-text-light) { 53 | $r: red($color); 54 | $g: green($color); 55 | $b: blue($color); 56 | 57 | $yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000; 58 | 59 | @if ($yiq >= $yiq-contrasted-threshold) { 60 | @return $dark; 61 | } @else { 62 | @return $light; 63 | } 64 | } 65 | 66 | // Retrieve color Sass maps 67 | @function color($key: "blue") { 68 | @return map-get($colors, $key); 69 | } 70 | 71 | @function theme-color($key: "primary") { 72 | @return map-get($theme-colors, $key); 73 | } 74 | 75 | @function gray($key: "100") { 76 | @return map-get($grays, $key); 77 | } 78 | 79 | // Request a theme color level 80 | @function theme-color-level($color-name: "primary", $level: 0) { 81 | $color: theme-color($color-name); 82 | $color-base: if($level > 0, $black, $white); 83 | $level: abs($level); 84 | 85 | @return mix($color-base, $color, $level * $theme-color-interval); 86 | } 87 | -------------------------------------------------------------------------------- /src/controllers/authController.js: -------------------------------------------------------------------------------- 1 | const bcryptjs = require("bcryptjs"); 2 | const db = require("../database/models"); 3 | const { validationResult } = require("express-validator"); 4 | 5 | const controller = { 6 | showLogin: function (req, res) { 7 | return res.render("auth/login"); 8 | }, 9 | login: async function (req, res) { 10 | //validar los datos 11 | let errores = validationResult(req); 12 | 13 | //si hay errores, retornarlos a la vista 14 | if (!errores.isEmpty()) { 15 | let errors = errores.mapped(); 16 | console.log(errors); 17 | return res.render("auth/login", { errors: errors, olds: req.body }); 18 | } 19 | 20 | //leo el json 21 | let user = await db.User.findOne({ 22 | where: { 23 | email: req.body.email, 24 | }, 25 | }); 26 | 27 | console.log(req.body); 28 | //buscar al usuario 29 | if (user) { 30 | let passOk = bcryptjs.compareSync(req.body.password, user.password); 31 | if (passOk) { 32 | //si la password es correcta se guarda el ususario en session 33 | req.session.userLogged = user; 34 | req.session.lastActitity = Date.now(); 35 | 36 | //si recordar usuario esta activado enviamos una cookie con el email 37 | if (req.body.rememberMe) { 38 | res.cookie("userId", user.id, { maxAge: 1000 * 60 * 5 }); 39 | } 40 | //redirigimos al menu de usuario 41 | return res.redirect("/profile"); 42 | } else { 43 | //si la password no es correcta devolvemos el error 44 | return res.render("auth/login", { 45 | errors: { 46 | password: { 47 | msg: "La contraseña no es válida.", 48 | }, 49 | }, 50 | olds: req.body, 51 | }); 52 | } 53 | } else { 54 | return res.render("auth/login", { 55 | errors: { email: { msg: "No se encontró el usuario", olds: req.body } }, 56 | }); 57 | } 58 | }, 59 | showRegister: function (req, res) { 60 | return res.render("auth/register"); 61 | }, 62 | register: async function (req, res) { 63 | //validar los datos 64 | let errores = validationResult(req); 65 | 66 | //si hay errores, retornarlos a la vista 67 | if (!errores.isEmpty()) { 68 | let errors = errores.mapped(); 69 | console.log(errors); 70 | return res.render("register", { errors: errors, olds: req.body }); 71 | } 72 | 73 | let data = { 74 | name: req.body.name, 75 | email: req.body.email, 76 | password: bcryptjs.hashSync(req.body.password, 10), 77 | }; 78 | //guarda el usuario en base de datos 79 | let newUser = await db.User.create(data); 80 | 81 | // SE LOGEA EN SESSION 82 | req.session.userLogged = newUser; 83 | 84 | //redirigimos a menu de usuario 85 | return res.redirect(`/profile/`); 86 | }, 87 | logout: function (req, res) { 88 | req.session.destroy(); 89 | res.clearCookie("userId"); 90 | return res.redirect("/"); 91 | }, 92 | profile: function (req, res) { 93 | return res.render("auth/profile"); 94 | }, 95 | }; 96 | 97 | module.exports = controller; 98 | -------------------------------------------------------------------------------- /public/js/addons/jquery.zmd.hierarchical-display.min.js: -------------------------------------------------------------------------------- 1 | (function(i){"use strict";function t(t){return this.each(function(){var a=i(this),e=a.data("zmd.hierarchicalDisplay"),o=i.extend({},n.DEFAULTS,a.data(),"object"==typeof t&&t);return e||a.data("zmd.hierarchicalDisplay",e=new n(this,o)),"string"==typeof t?e[t]():o.action in e?e[o.action]():void 0})}var n=function(t,a){this.$element=i(t),this.$children=this.$element.children(),this.options=i.extend({},n.DEFAULTS,a),this._time=n.TRANSITION_DURATION*this.options.speed,this.init(),!0===this.options.debug&&this._debug()};n.VERSION="1.0.1",n.TRANSITION_DURATION=300,n.DEFAULTS={action:"show",speed:5,animationIn:"zoomedIn",animationOut:"zoomedOut",debug:!1},n.prototype.init=function(){var t,n,a,e=this,o=this.$element,s=this.$children,h=this.options,l=this._time;o.addClass("zmd-hierarchical-display"),s.each(function(){t=i(this).position(),n=.8*t.left+t.top,a=parseFloat(n/l).toFixed(2),i(this).css("-webkit-animation-delay",a+"s").css("animation-delay",a+"s")}),this._delay=a,s.last().on("webkitAnimationEnd animationend",function(){i(this).hasClass(h.animationOut)&&e._complete("hidden"),i(this).hasClass(h.animationIn)&&e._complete("shown")})},n.prototype.show=function(){var t=this.$element,n=(this.$children,this.options);t.hasClass("in")||t.hasClass("zmd-hierarchical-displaying")||(this._removeAnimations(),t.trigger(i.Event("show.zmd.hierarchicalDisplay")),this._addAnimation(n.animationIn))},n.prototype.hide=function(){var t=this.$element,n=(this.$children,this.options);"hidden"===t.css("visibility")||t.hasClass("zmd-hierarchical-displaying")||(this._removeAnimations(),t.trigger(i.Event("hide.zmd.hierarchicalDisplay")),this._addAnimation(n.animationOut))},n.prototype.toggle=function(){return this.$element.hasClass("in")?this.hide():this.show()},n.prototype._removeAnimations=function(){var t=this.options;this.$children.each(function(){i(this).removeClass(t.animationIn).removeClass(t.animationOut)})},n.prototype._addAnimation=function(t){this.$element.addClass("zmd-hierarchical-displaying"),this.$children.each(function(){i(this).addClass(t).addClass("animation")})},n.prototype._complete=function(t){this.$element.removeClass("zmd-hierarchical-displaying").toggleClass("in").trigger(i.Event(t+".zmd.hierarchicalDisplay"))},n.prototype._debug=function(){i(document).on("show.zmd.hierarchicalDisplay",function(i){console.log('Event "show.zmd.hierarchicalDisplay". For more information see:'),console.log(i)}).on("shown.zmd.hierarchicalDisplay",function(i){console.log('Event "shown.zmd.hierarchicalDisplay". For more information see:'),console.log(i)}).on("hide.zmd.hierarchicalDisplay",function(i){console.log('Event "hide.zmd.hierarchicalDisplay". For more information see:'),console.log(i)}).on("hidden.zmd.hierarchicalDisplay",function(i){console.log('Event "hidden.zmd.hierarchicalDisplay". For more information see:'),console.log(i)})},i.fn.hierarchicalDisplay=t,i.fn.hierarchicalDisplay.Constructor=n,i(document).on("ready",function(){i('[data-animation="hierarchical-display"]').each(function(){t.call(i(this))})}),i(document).on("click",'[data-toggle="hierarchical-display"]',function(n){var a=i(this),e=i(a.attr("data-target")||a.attr("href"));a.is("a")&&n.preventDefault(),t.call(e,"toggle")})})(jQuery); -------------------------------------------------------------------------------- /public/scss/free/_msc.scss: -------------------------------------------------------------------------------- 1 | // Miscellaneous 2 | // Edge Headers 3 | .edge-header { 4 | display: block; 5 | height: $edge-header-height; 6 | background-color: $edge-header-background-color; 7 | } 8 | 9 | .free-bird { 10 | margin-top: $edge-header-margin-top; 11 | } 12 | 13 | // Additional gradients 14 | .juicy-peach-gradient { 15 | background-image: linear-gradient(to right, #ffecd2 0%, #fcb69f 100%); 16 | } 17 | 18 | .young-passion-gradient { 19 | background-image: linear-gradient(to right, #ff8177 0%, #ff867a 0%, #ff8c7f 21%, #f99185 52%, #cf556c 78%, #b12a5b 100%); 20 | } 21 | 22 | .lady-lips-gradient { 23 | background-image: linear-gradient(to top, #ff9a9e 0%, #fecfef 99%, #fecfef 100%); 24 | } 25 | 26 | .sunny-morning-gradient { 27 | background-image: linear-gradient(120deg, #f6d365 0%, #fda085 100%); 28 | } 29 | 30 | .rainy-ashville-gradient { 31 | background-image: linear-gradient(to top, #fbc2eb 0%, #a6c1ee 100%); 32 | } 33 | 34 | .frozen-dreams-gradient { 35 | background-image: linear-gradient(to top, #fdcbf1 0%, #fdcbf1 1%, #e6dee9 100%); 36 | } 37 | 38 | .warm-flame-gradient { 39 | background-image: linear-gradient(45deg, #ff9a9e 0%, #fad0c4 99%, #fad0c4 100%); 40 | } 41 | 42 | .night-fade-gradient { 43 | background-image: linear-gradient(to top, #a18cd1 0%, #fbc2eb 100%); 44 | } 45 | 46 | .spring-warmth-gradient { 47 | background-image: linear-gradient(to top, #fad0c4 0%, #ffd1ff 100%); 48 | } 49 | 50 | .winter-neva-gradient { 51 | background-image: linear-gradient(120deg, #a1c4fd 0%, #c2e9fb 100%); 52 | } 53 | 54 | .dusty-grass-gradient { 55 | background-image: linear-gradient(120deg, #d4fc79 0%, #96e6a1 100%); 56 | } 57 | 58 | .tempting-azure-gradient { 59 | background-image: linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%); 60 | } 61 | 62 | .heavy-rain-gradient { 63 | background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); 64 | } 65 | 66 | .amy-crisp-gradient { 67 | background-image: linear-gradient(120deg, #a6c0fe 0%, #f68084 100%); 68 | } 69 | 70 | .mean-fruit-gradient { 71 | background-image: linear-gradient(120deg, #fccb90 0%, #d57eeb 100%); 72 | } 73 | 74 | .deep-blue-gradient { 75 | background-image: linear-gradient(120deg, #e0c3fc 0%, #8ec5fc 100%); 76 | } 77 | 78 | .ripe-malinka-gradient { 79 | background-image: linear-gradient(120deg, #f093fb 0%, #f5576c 100%); 80 | } 81 | 82 | .cloudy-knoxville-gradient { 83 | background-image: linear-gradient(120deg, #fdfbfb 0%, #ebedee 100%); 84 | } 85 | 86 | .morpheus-den-gradient { 87 | background-image: linear-gradient(to top, #30cfd0 0%, #330867 100%); 88 | } 89 | 90 | .rare-wind-gradient { 91 | background-image: linear-gradient(to top, #a8edea 0%, #fed6e3 100%); 92 | } 93 | 94 | .near-moon-gradient { 95 | background-image: linear-gradient(to top, #5ee7df 0%, #b490ca 100%); 96 | } 97 | 98 | .schedule-list { 99 | .hr-bold { 100 | border-top: 2px solid #212529; 101 | } 102 | 103 | .font-smaller { 104 | font-size: .8rem; 105 | } 106 | } 107 | 108 | .note { 109 | padding: 10px; 110 | border-left: 6px solid; 111 | border-radius: 5px; 112 | strong { 113 | font-weight: 600; 114 | } 115 | p { 116 | font-weight: 500; 117 | } 118 | } 119 | 120 | @each $name, $color in $note { 121 | .note-#{$name} { 122 | background-color: map-get($color, bgc); 123 | border-color: map-get($color, border-color); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/views/auth/profile.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- include('../partials/head') %> 6 | 7 | 8 | 9 | 10 | <%- include('../partials/header') %> 11 | 12 | 13 |
14 |
15 |

16 | Perfil de <%= locals.userLogged.name%> 17 |

18 |
19 |
20 | Mis Datos 21 |
22 |
23 |

Nombre: 24 | <%= locals.userLogged.name %> 25 |

26 |

Email: 27 | <%= locals.userLogged.email %> 28 |

29 | Editar mi perfil 30 | Salir 31 |
32 |
33 |
34 |
35 | Mis Pedidos 36 |
37 |
38 | 39 | 40 | 41 | 44 | 47 | 50 | 53 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
42 | # 43 | 45 | Fecha 46 | 48 | Envío 49 | 51 | Pago 52 | 54 | Total 55 |
12022/06/04 10:00Correo ArgentinoEfectivo$1220
71 |
72 |
73 |
74 |
75 | 76 | <%- include('../partials/footer') %> 77 | <%- include('../partials/scripts') %> 78 | 79 | 80 | -------------------------------------------------------------------------------- /public/scss/core/_typography.scss: -------------------------------------------------------------------------------- 1 | // Typography 2 | 3 | // Roboto font 4 | @font-face { 5 | font-family: Roboto; 6 | font-weight: 200; 7 | src: local(Roboto Thin), url("#{$roboto-font-path}Roboto-Thin.eot"); 8 | src: url("#{$roboto-font-path}Roboto-Thin.eot?#iefix") format("embedded-opentype"), url("#{$roboto-font-path}Roboto-Thin.woff2") format("woff2"), url("#{$roboto-font-path}Roboto-Thin.woff") format("woff"), url("#{$roboto-font-path}Roboto-Thin.ttf") format("truetype"); 9 | } 10 | 11 | @font-face { 12 | font-family: Roboto; 13 | font-weight: 300; 14 | src: local(Roboto Light), url("#{$roboto-font-path}Roboto-Light.eot"); 15 | src: url("#{$roboto-font-path}Roboto-Light.eot?#iefix") format("embedded-opentype"), url("#{$roboto-font-path}Roboto-Light.woff2") format("woff2"), url("#{$roboto-font-path}Roboto-Light.woff") format("woff"), url("#{$roboto-font-path}Roboto-Light.ttf") format("truetype"); 16 | } 17 | 18 | @font-face { 19 | font-family: Roboto; 20 | font-weight: 400; 21 | src: local(Roboto Regular), url("#{$roboto-font-path}Roboto-Regular.eot"); 22 | src: url("#{$roboto-font-path}Roboto-Regular.eot?#iefix") format("embedded-opentype"), url("#{$roboto-font-path}Roboto-Regular.woff2") format("woff2"), url("#{$roboto-font-path}Roboto-Regular.woff") format("woff"), url("#{$roboto-font-path}Roboto-Regular.ttf") format("truetype"); 23 | } 24 | 25 | @font-face { 26 | font-family: Roboto; 27 | font-weight: 500; 28 | src: url("#{$roboto-font-path}Roboto-Medium.eot"); 29 | src: url("#{$roboto-font-path}Roboto-Medium.eot?#iefix") format("embedded-opentype"), url("#{$roboto-font-path}Roboto-Medium.woff2") format("woff2"), url("#{$roboto-font-path}Roboto-Medium.woff") format("woff"), url("#{$roboto-font-path}Roboto-Medium.ttf") format("truetype"); 30 | } 31 | 32 | @font-face { 33 | font-family: Roboto; 34 | font-weight: 700; 35 | src: url("#{$roboto-font-path}Roboto-Bold.eot"); 36 | src: url("#{$roboto-font-path}Roboto-Bold.eot?#iefix") format("embedded-opentype"), url("#{$roboto-font-path}Roboto-Bold.woff2") format("woff2"), url("#{$roboto-font-path}Roboto-Bold.woff") format("woff"), url("#{$roboto-font-path}Roboto-Bold.ttf") format("truetype"); 37 | } 38 | 39 | // General properties 40 | body { 41 | font-family: $mdb-font-family; 42 | font-weight: $font-weight-light; 43 | } 44 | 45 | h1, h2, h3, h4, h5, h6 { 46 | font-weight: $font-weight-light; 47 | } 48 | 49 | // Blockquote 50 | .blockquote { 51 | padding: $blockquote-padding-y $blockquote-padding-x; 52 | border-left: .25rem solid #eceeef; 53 | &.text-right { 54 | border-right: .25rem solid #eceeef; 55 | border-left: none; 56 | } 57 | .bq-title { 58 | margin-bottom: 0; 59 | font-size: $font-size-large; 60 | font-weight: 400; 61 | } 62 | p { 63 | padding: $blockquote-p-padding-y 0; 64 | font-size: $blockquote-p-font-size; 65 | } 66 | } 67 | 68 | @each $name, $color in $basic { 69 | .bq-#{$name} { 70 | border-left: 3px solid $color !important; 71 | .bq-title { 72 | color: $color !important; 73 | } 74 | } 75 | } 76 | 77 | // Responsive headings 78 | @each $key, $val in $grid-breakpoints { 79 | @include media-breakpoint-up($key) { 80 | $y: map-get($responsive-headings, $key); 81 | @each $name, $value in $y { 82 | .#{$name}-responsive { 83 | font-size: $value; 84 | } 85 | } 86 | } 87 | } 88 | 89 | @each $name, $color in $basic-mdb-colors { 90 | @include text-emphasis-variant(".text-#{$name}", $color); 91 | } 92 | 93 | .font-small { 94 | font-size: $font-size-small; 95 | } 96 | -------------------------------------------------------------------------------- /public/css/addons/datatables.min.css: -------------------------------------------------------------------------------- 1 | div.dataTables_wrapper div.dataTables_filter input,div.dataTables_wrapper div.dataTables_filter select,div.dataTables_wrapper div.dataTables_length input,div.dataTables_wrapper div.dataTables_length select{width:auto}table.dataTable thead{cursor:pointer}div.dataTables_wrapper div.dataTables_length.d-flex.flex-row label{margin-top:1.2rem;margin-right:1rem}div.dataTables_wrapper div.dataTables_length.d-flex.flex-row .select-wrapper.mdb-select .select-dropdown,div.dataTables_wrapper div.dataTables_length.d-flex.flex-row .select-wrapper.mdb-select span{margin-top:1rem}div.dataTables_wrapper div.dataTables_filter label,div.dataTables_wrapper div.dataTables_length label{text-align:left;font-weight:400;padding-top:.5rem;padding-bottom:.5rem}div.dataTables_wrapper div.dataTables_filter{text-align:right}div.dataTables_wrapper div.dataTables_filter input{margin-left:.5rem;display:inline-block}div.dataTables_wrapper div.dataTables_info,div.dataTables_wrapper div.dataTables_paginate{font-weight:400;padding-top:1rem;padding-bottom:1rem}div.dataTables_wrapper div.dataTables_paginate{text-align:right;margin:0}div.dataTables_wrapper div.dataTables_paginate ul.pagination{-webkit-box-pack:end;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item.active .page-link:focus{background-color:#4285f4}div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item .page-link:focus{-webkit-box-shadow:none;box-shadow:none}@media (max-width:767px){div.dataTables_wrapper div .dataTables_filter,div.dataTables_wrapper div .dataTables_info,div.dataTables_wrapper div .dataTables_length,div.dataTables_wrapper div .dataTables_paginate ul.pagination{text-align:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}}.bs-select select{display:inline-block!important}table.dataTable thead>tr>td.sorting,table.dataTable thead>tr>td.sorting_asc,table.dataTable thead>tr>td.sorting_desc,table.dataTable thead>tr>th.sorting,table.dataTable thead>tr>th.sorting_asc,table.dataTable thead>tr>th.sorting_desc{padding-right:30px}table.dataTable thead>tr>td:active,table.dataTable thead>tr>th:active{outline:0}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_desc_disabled{cursor:pointer;position:relative}table.dataTable thead .sorting:after,table.dataTable thead .sorting:before,table.dataTable thead .sorting_asc:after,table.dataTable thead .sorting_asc:before,table.dataTable thead .sorting_asc_disabled:after,table.dataTable thead .sorting_asc_disabled:before,table.dataTable thead .sorting_desc:after,table.dataTable thead .sorting_desc:before,table.dataTable thead .sorting_desc_disabled:after,table.dataTable thead .sorting_desc_disabled:before{position:absolute;bottom:.9em;display:block;opacity:.3;font-family:'Font Awesome\ 5 Free';font-weight:900;font-size:1rem}table.dataTable thead .sorting:before,table.dataTable thead .sorting_asc:before,table.dataTable thead .sorting_asc_disabled:before,table.dataTable thead .sorting_desc:before,table.dataTable thead .sorting_desc_disabled:before{right:1em;content:"\f0de"}table.dataTable thead .sorting:after,table.dataTable thead .sorting_asc:after,table.dataTable thead .sorting_asc_disabled:after,table.dataTable thead .sorting_desc:after,table.dataTable thead .sorting_desc_disabled:after{content:"\f0dd";right:16px}table.dataTable thead .sorting_asc:before,table.dataTable thead .sorting_desc:after{opacity:1}table.dataTable thead .sorting_asc_disabled:before,table.dataTable thead .sorting_desc_disabled:after{opacity:0} -------------------------------------------------------------------------------- /src/views/partials/header.ejs: -------------------------------------------------------------------------------- 1 | 2 | 79 | -------------------------------------------------------------------------------- /public/scss/free/_animations-basic.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * animate.css -http://daneden.me/animate 3 | * Version - 3.7.0 4 | * Licensed under the MIT license - http://opensource.org/licenses/MIT 5 | * 6 | * Copyright (c) 2018 Daniel Eden 7 | */ 8 | .animated { 9 | animation-duration: 1s; 10 | animation-fill-mode: both; 11 | &.infinite { 12 | animation-iteration-count: infinite; 13 | } 14 | &.delay-1s { 15 | animation-delay: 1s; 16 | } 17 | &.delay-2s { 18 | animation-delay: 2s; 19 | } 20 | &.delay-3s { 21 | animation-delay: 3s; 22 | } 23 | &.delay-4s { 24 | animation-delay: 4s; 25 | } 26 | &.delay-5s { 27 | animation-delay: 5s; 28 | } 29 | &.fast { 30 | animation-duration: 800ms; 31 | } 32 | &.faster { 33 | animation-duration: 500ms; 34 | } 35 | &.slow { 36 | animation-duration: 2s; 37 | } 38 | &.slower { 39 | animation-duration: 3s; 40 | } 41 | } 42 | 43 | @media (prefers-reduced-motion) { 44 | .animated { 45 | transition: none !important; 46 | animation: unset !important; 47 | } 48 | } 49 | 50 | @keyframes fadeIn { 51 | from { 52 | opacity: 0; 53 | } 54 | 55 | to { 56 | opacity: 1; 57 | } 58 | } 59 | 60 | .fadeIn { 61 | animation-name: fadeIn; 62 | } 63 | 64 | @keyframes fadeInDown { 65 | from { 66 | opacity: 0; 67 | transform: translate3d(0, -100%, 0); 68 | } 69 | 70 | to { 71 | opacity: 1; 72 | transform: translate3d(0, 0, 0); 73 | } 74 | } 75 | 76 | .fadeInDown { 77 | animation-name: fadeInDown; 78 | } 79 | 80 | @keyframes fadeInLeft { 81 | from { 82 | opacity: 0; 83 | transform: translate3d(-100%, 0, 0); 84 | } 85 | 86 | to { 87 | opacity: 1; 88 | transform: translate3d(0, 0, 0); 89 | } 90 | } 91 | 92 | .fadeInLeft { 93 | animation-name: fadeInLeft; 94 | } 95 | 96 | 97 | @keyframes fadeInRight { 98 | from { 99 | opacity: 0; 100 | transform: translate3d(100%, 0, 0); 101 | } 102 | 103 | to { 104 | opacity: 1; 105 | transform: translate3d(0, 0, 0); 106 | } 107 | } 108 | 109 | .fadeInRight { 110 | animation-name: fadeInRight; 111 | } 112 | 113 | 114 | @keyframes fadeInUp { 115 | from { 116 | opacity: 0; 117 | transform: translate3d(0, 100%, 0); 118 | } 119 | 120 | to { 121 | opacity: 1; 122 | transform: translate3d(0, 0, 0); 123 | } 124 | } 125 | 126 | .fadeInUp { 127 | animation-name: fadeInUp; 128 | } 129 | 130 | 131 | @keyframes fadeOut { 132 | from { 133 | opacity: 1; 134 | } 135 | 136 | to { 137 | opacity: 0; 138 | } 139 | } 140 | 141 | .fadeOut { 142 | animation-name: fadeOut; 143 | } 144 | 145 | 146 | @keyframes fadeOutDown { 147 | from { 148 | opacity: 1; 149 | } 150 | 151 | to { 152 | opacity: 0; 153 | transform: translate3d(0, 100%, 0); 154 | } 155 | } 156 | 157 | .fadeOutDown { 158 | animation-name: fadeOutDown; 159 | } 160 | 161 | 162 | @keyframes fadeOutLeft { 163 | from { 164 | opacity: 1; 165 | } 166 | 167 | to { 168 | opacity: 0; 169 | transform: translate3d(-100%, 0, 0); 170 | } 171 | } 172 | 173 | .fadeOutLeft { 174 | animation-name: fadeOutLeft; 175 | } 176 | 177 | 178 | @keyframes fadeOutRight { 179 | from { 180 | opacity: 1; 181 | } 182 | 183 | to { 184 | opacity: 0; 185 | transform: translate3d(100%, 0, 0); 186 | } 187 | } 188 | 189 | .fadeOutRight { 190 | animation-name: fadeOutRight; 191 | } 192 | 193 | 194 | @keyframes fadeOutUp { 195 | from { 196 | opacity: 1; 197 | } 198 | 199 | to { 200 | opacity: 0; 201 | transform: translate3d(0, -100%, 0); 202 | } 203 | } 204 | 205 | .fadeOutUp { 206 | animation-name: fadeOutUp; 207 | } 208 | -------------------------------------------------------------------------------- /public/scss/core/_global.scss: -------------------------------------------------------------------------------- 1 | // Globals 2 | // Full palette of colors 3 | @each $color_name, $color in $mdb-colors-1 { 4 | @each $color_type, $color_value in $color { 5 | @if $color_type == "base" { 6 | .#{$color_name} { 7 | background-color: $color_value !important; 8 | } 9 | .#{$color_name}-text { 10 | color: $color-value !important; 11 | } 12 | .rgba-#{$color_name}-slight, 13 | .rgba-#{$color_name}-slight:after { 14 | background-color: rgba($color_value, .1); 15 | } 16 | .rgba-#{$color_name}-light, 17 | .rgba-#{$color_name}-light:after { 18 | background-color: rgba($color_value, .3); 19 | } 20 | .rgba-#{$color_name}-strong, 21 | .rgba-#{$color_name}-strong:after { 22 | background-color: rgba($color_value, .7); 23 | } 24 | } 25 | @else { 26 | @if $enable_full_palette { 27 | .#{$color_name}.#{$color_type} { 28 | background-color: $color_value !important; 29 | } 30 | } 31 | } 32 | } 33 | } 34 | 35 | // Stylish color 36 | @each $color_name, $color_value in $stylish-rgba { 37 | .#{$color_name} { 38 | background-color: $color_value; 39 | } 40 | } 41 | 42 | // Material colors palette 43 | @each $color_name, $color in $material-colors { 44 | .#{$color_name} { 45 | background-color: $color !important; 46 | } 47 | } 48 | 49 | // Basic gradients 50 | @each $name, $val in $gradients { 51 | @include make-gradient($name, $val); 52 | } 53 | @each $name, $val in $gradients-rgba { 54 | @include make-gradient-rgba($name, $val); 55 | } 56 | 57 | .dark-grey-text { 58 | color: #4f4f4f !important; 59 | &:hover, 60 | &:focus { 61 | color: #4f4f4f !important; 62 | } 63 | } 64 | 65 | // Shadow on hover 66 | .hoverable { 67 | box-shadow: none; 68 | transition: $transition-hoverable; 69 | &:hover { 70 | box-shadow: $z-depth-2; 71 | transition: $transition-hoverable; 72 | } 73 | } 74 | 75 | // Shadows 76 | .z-depth-0 { 77 | box-shadow: none !important; 78 | } 79 | .z-depth-1 { 80 | box-shadow: $z-depth-1 !important; 81 | } 82 | .z-depth-1-half { 83 | box-shadow: $z-depth-1-half !important; 84 | } 85 | .z-depth-2 { 86 | box-shadow: $z-depth-2 !important; 87 | } 88 | .z-depth-3 { 89 | box-shadow: $z-depth-3 !important; 90 | } 91 | .z-depth-4 { 92 | box-shadow: $z-depth-4 !important; 93 | } 94 | .z-depth-5 { 95 | box-shadow: $z-depth-5 !important; 96 | } 97 | 98 | // Disabled cursor 99 | .disabled, 100 | :disabled { 101 | pointer-events: none !important; 102 | } 103 | 104 | // Links 105 | a { 106 | color: $link-color; 107 | text-decoration: none; 108 | cursor: pointer; 109 | transition: $transition-basic; 110 | &:hover { 111 | color: $link-hover-color; 112 | text-decoration: none; 113 | transition: $transition-basic; 114 | } 115 | &.disabled, 116 | &:disabled { 117 | &:hover { 118 | color: $link-color; 119 | } 120 | } 121 | } 122 | 123 | a:not([href]):not([tabindex]), a:not([href]):not([tabindex]):focus, a:not([href]):not([tabindex]):hover { 124 | color: inherit; 125 | text-decoration: none; 126 | } 127 | 128 | // Divider 129 | .divider-new { 130 | display: flex; 131 | flex-direction: row; 132 | align-items: center; 133 | justify-content: center; 134 | margin-top: $divider-margin-y; 135 | margin-bottom: $divider-margin-y; 136 | > h1, h2, h3, h4, h5, h6 { 137 | margin-bottom: 0; 138 | } 139 | &:before, 140 | &:after { 141 | flex: 1; 142 | height: 1.5px; 143 | height: $divider-height; 144 | content: ""; 145 | background: #c6c6c6; 146 | } 147 | &:before { 148 | margin: 0 $divider-margin-x 0 0; 149 | } 150 | &:after { 151 | margin: 0 0 0 $divider-margin-x; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /public/css/addons/datatables-select.min.css: -------------------------------------------------------------------------------- 1 | table.dataTable tbody>tr.selected,table.dataTable tbody>tr>.selected{background-color:#B0BED9}table.dataTable.display tbody>tr.odd.selected,table.dataTable.display tbody>tr.odd>.selected,table.dataTable.stripe tbody>tr.odd.selected,table.dataTable.stripe tbody>tr.odd>.selected{background-color:#acbad4}table.dataTable.display tbody>tr.selected:hover,table.dataTable.display tbody>tr>.selected:hover,table.dataTable.hover tbody>tr.selected:hover,table.dataTable.hover tbody>tr>.selected:hover{background-color:#aab7d1}table.dataTable.display tbody>tr.selected>.sorting_1,table.dataTable.display tbody>tr.selected>.sorting_2,table.dataTable.display tbody>tr.selected>.sorting_3,table.dataTable.display tbody>tr>.selected,table.dataTable.order-column tbody>tr.selected>.sorting_1,table.dataTable.order-column tbody>tr.selected>.sorting_2,table.dataTable.order-column tbody>tr.selected>.sorting_3,table.dataTable.order-column tbody>tr>.selected{background-color:#acbad5}table.dataTable.display tbody>tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_1{background-color:#a6b4cd}table.dataTable.display tbody>tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody>tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody>tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_1{background-color:#acbad5}table.dataTable.display tbody>tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody>tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody>tr.odd>.selected,table.dataTable.order-column.stripe tbody>tr.odd>.selected{background-color:#a6b4cd}table.dataTable.display tbody>tr.even>.selected,table.dataTable.order-column.stripe tbody>tr.even>.selected{background-color:#acbad5}table.dataTable.display tbody>tr.selected:hover>.sorting_1,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_1{background-color:#a2aec7}table.dataTable.display tbody>tr.selected:hover>.sorting_2,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody>tr.selected:hover>.sorting_3,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_3{background-color:#a5b2cb}table.dataTable.display tbody>tr:hover>.selected,table.dataTable.display tbody>tr>.selected:hover,table.dataTable.order-column.hover tbody>tr:hover>.selected,table.dataTable.order-column.hover tbody>tr>.selected:hover{background-color:#a2aec7}table.dataTable tbody td.select-checkbox,table.dataTable tbody th.select-checkbox{position:relative}table.dataTable tbody td.select-checkbox:after,table.dataTable tbody td.select-checkbox:before,table.dataTable tbody th.select-checkbox:after,table.dataTable tbody th.select-checkbox:before{display:block;position:absolute;top:1.2em;left:50%;width:12px;height:12px;-webkit-box-sizing:border-box;box-sizing:border-box}table.dataTable tbody td.select-checkbox:before,table.dataTable tbody th.select-checkbox:before{content:' ';margin-top:4px;margin-left:-6px;border:1px solid #000;-webkit-border-radius:3px;border-radius:3px}table.dataTable tr.selected td.select-checkbox:after,table.dataTable tr.selected th.select-checkbox:after{content:'\2714';margin-top:0;margin-left:-4px;text-align:center;text-shadow:1px 1px #B0BED9,-1px -1px #B0BED9,1px -1px #B0BED9,-1px 1px #B0BED9}div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:.5em}@media screen and (max-width:640px){div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:0;display:block}} -------------------------------------------------------------------------------- /public/scss/addons/_datatables.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * MDBootstrap integration with Datatables 3 | * Learn more: https://mdbootstrap.com/docs/jquery/tables/datatables/ 4 | * About MDBootstrap: https://mdbootstrap.com/ 5 | * 6 | * This combined file was created by the DataTables downloader builder: 7 | * https://datatables.net/download 8 | * 9 | * To rebuild or modify this file with the latest versions of the included 10 | * software please visit: 11 | * https://datatables.net/download/#bs4/dt-1.10.18 12 | * 13 | * Included libraries: 14 | * DataTables 1.10.18 15 | */ 16 | 17 | div.dataTables_wrapper div { 18 | &.dataTables_length { 19 | select, input { 20 | width: auto; 21 | } 22 | &.d-flex.flex-row { 23 | label { 24 | margin-top: 1.2rem; 25 | margin-right: 1rem; 26 | } 27 | .select-wrapper.mdb-select { 28 | span, .select-dropdown { 29 | margin-top: 1rem; 30 | } 31 | } 32 | } 33 | } 34 | &.dataTables_length, 35 | &.dataTables_filter { 36 | label { 37 | padding-top: .5rem; 38 | padding-bottom: .5rem; 39 | font-weight: 400; 40 | text-align: left; 41 | } 42 | } 43 | &.dataTables_filter { 44 | select, 45 | input { 46 | width: auto; 47 | } 48 | input { 49 | display: inline-block; 50 | margin-left: .5rem; 51 | } 52 | text-align: right; 53 | } 54 | &.dataTables_info, 55 | &.dataTables_paginate { 56 | padding-top: 1rem; 57 | padding-bottom: 1rem; 58 | font-weight: 400; 59 | } 60 | &.dataTables_paginate { 61 | margin: 0; 62 | text-align: right; 63 | ul.pagination { 64 | -ms-flex-pack: end; 65 | -webkit-justify-content: flex-end; 66 | justify-content: flex-end; 67 | -webkit-box-pack: end; 68 | .page-item { 69 | &.active .page-link:focus { 70 | background-color: #4285f4; 71 | } 72 | .page-link:focus { 73 | -webkit-box-shadow: none; 74 | box-shadow: none; 75 | } 76 | } 77 | } 78 | } 79 | } 80 | 81 | @media (max-width: 767px) { 82 | div.dataTables_wrapper div { 83 | .dataTables_length, .dataTables_filter, .dataTables_info, .dataTables_paginate ul.pagination { 84 | -ms-flex-pack: center; 85 | -webkit-justify-content: center; 86 | justify-content: center; 87 | text-align: center; 88 | -webkit-box-pack: center; 89 | } 90 | } 91 | } 92 | 93 | .bs-select select { 94 | display: inline-block !important; 95 | } 96 | 97 | table.dataTable thead { 98 | cursor: pointer; 99 | > tr > { 100 | th, 101 | td { 102 | &.sorting_asc, &.sorting_desc, &.sorting { 103 | padding-right: 30px; 104 | } 105 | } 106 | th:active, td:active { 107 | outline: none; 108 | } 109 | } 110 | .sorting, 111 | .sorting_asc, 112 | .sorting_desc, 113 | .sorting_asc_disabled, 114 | .sorting_desc_disabled { 115 | position: relative; 116 | cursor: pointer; 117 | &:before, &:after { 118 | position: absolute; 119 | bottom: .9em; 120 | display: block; 121 | opacity: .3; 122 | } 123 | } 124 | .sorting:before, .sorting_asc:before, .sorting_desc:before, .sorting_asc_disabled:before, .sorting_desc_disabled:before { 125 | right: 1em; 126 | font-family: "Font Awesome\ 5 Free", sans-serif; 127 | font-size: 1rem; 128 | font-weight: 900; 129 | content: "\f0de"; 130 | } 131 | .sorting:after, .sorting_asc:after, .sorting_desc:after, .sorting_asc_disabled:after, .sorting_desc_disabled:after { 132 | right: 16px; 133 | font-family: "Font Awesome\ 5 Free", sans-serif; 134 | font-size: 1rem; 135 | font-weight: 900; 136 | content: "\f0dd"; 137 | } 138 | .sorting_asc:before, .sorting_desc:after { 139 | opacity: 1; 140 | } 141 | .sorting_asc_disabled:before, .sorting_desc_disabled:after { 142 | opacity: 0; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /public/scss/core/_waves.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Waves v0.7.6 3 | * http://fian.my.id/Waves 4 | * 5 | * Copyright 2014-2018 Alfiana E. Sibuea and other contributors 6 | * Released under the MIT license 7 | * https://github.com/fians/Waves/blob/master/LICENSE */ 8 | 9 | @mixin waves-transition($transition){ 10 | -webkit-transition: $transition; 11 | -moz-transition: $transition; 12 | -o-transition: $transition; 13 | transition: $transition; 14 | } 15 | 16 | @mixin waves-transform($string){ 17 | -webkit-transform: $string; 18 | -moz-transform: $string; 19 | -ms-transform: $string; 20 | -o-transform: $string; 21 | transform: $string; 22 | } 23 | 24 | @mixin waves-box-shadow($shadow){ 25 | -webkit-box-shadow: $shadow; 26 | box-shadow: $shadow; 27 | } 28 | 29 | .waves-effect { 30 | position: relative; 31 | overflow: hidden; 32 | cursor: pointer; 33 | -webkit-user-select: none; 34 | -moz-user-select: none; 35 | -ms-user-select: none; 36 | user-select: none; 37 | -webkit-tap-highlight-color: transparent; 38 | 39 | .waves-ripple { 40 | $gradient: rgba(0, 0, 0, .2) 0,rgba(0, 0, 0, .3) 40%,rgba(0, 0, 0, .4) 50%,rgba(0, 0, 0, .5) 60%,rgba(255, 255, 255, 0) 70%; 41 | position: absolute; 42 | width: 100px; 43 | height: 100px; 44 | margin-top: -50px; 45 | margin-left: -50px; 46 | pointer-events: none; 47 | background: rgba(0, 0, 0, .2); 48 | background: radial-gradient($gradient); 49 | border-radius: 50%; 50 | opacity: 0; 51 | -webkit-transition-property: -webkit-transform, opacity; 52 | -moz-transition-property: -moz-transform, opacity; 53 | -o-transition-property: -o-transform, opacity; 54 | transition-property: transform, opacity; 55 | @include waves-transition(all .5s ease-out); 56 | @include waves-transform(scale(0) translate(0,0)); 57 | } 58 | 59 | &.waves-light .waves-ripple { 60 | $gradient: rgba(255, 255, 255, .2) 0,rgba(255, 255, 255, .3) 40%,rgba(255, 255, 255, .4) 50%,rgba(255, 255, 255, .5) 60%,rgba(255, 255, 255, 0) 70%; 61 | background: rgba(255, 255, 255, .4); 62 | background: radial-gradient($gradient); 63 | } 64 | 65 | &.waves-classic .waves-ripple { 66 | background: rgba(0, 0, 0, .2); 67 | } 68 | 69 | &.waves-classic.waves-light .waves-ripple { 70 | background: rgba(255, 255, 255, .4); 71 | } 72 | } 73 | 74 | .waves-notransition { 75 | @include waves-transition(none #{"!important"}); 76 | } 77 | 78 | .waves-button, 79 | .waves-circle { 80 | @include waves-transform(translateZ(0)); 81 | -webkit-mask-image: -webkit-radial-gradient(circle, #fff 100%, #000 100%); 82 | } 83 | 84 | .waves-button, 85 | .waves-button:hover, 86 | .waves-button:visited, 87 | .waves-button-input { 88 | z-index: 1; 89 | font-size: 1em; 90 | line-height: 1em; 91 | color: inherit; 92 | text-align: center; 93 | text-decoration: none; 94 | white-space: nowrap; 95 | vertical-align: middle; 96 | cursor: pointer; 97 | background-color: rgba(0, 0, 0, 0); 98 | border: none; 99 | outline: none; 100 | } 101 | 102 | .waves-button { 103 | padding: .85em 1.1em; 104 | border-radius: .2em; 105 | } 106 | 107 | .waves-button-input { 108 | padding: .85em 1.1em; 109 | margin: 0; 110 | } 111 | 112 | .waves-input-wrapper { 113 | position: relative; 114 | display: inline-block; 115 | vertical-align: middle; 116 | border-radius: .2em; 117 | 118 | &.waves-button { 119 | padding: 0; 120 | } 121 | 122 | .waves-button-input { 123 | position: relative; 124 | top: 0; 125 | left: 0; 126 | z-index: 1; 127 | } 128 | } 129 | 130 | .waves-circle { 131 | width: 2.5em; 132 | height: 2.5em; 133 | line-height: 2.5em; 134 | text-align: center; 135 | border-radius: 50%; 136 | } 137 | 138 | .waves-float { 139 | -webkit-mask-image: none; 140 | @include waves-box-shadow(0 1px 1.5px 1px rgba(0, 0, 0, .12)); 141 | @include waves-transition(all 300ms); 142 | 143 | &:active { 144 | @include waves-box-shadow(0 8px 20px 1px rgba(0, 0, 0, .3)); 145 | } 146 | } 147 | 148 | .waves-block { 149 | display: block; 150 | } 151 | 152 | a { 153 | &.waves-effect, 154 | &.waves-light { 155 | display: inline-block; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /public/js/addons/rating.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | $.fn.mdbRate = function () { 3 | var $stars; 4 | // Custom whitelist to allow for using HTML tags in popover content 5 | var myDefaultWhiteList = $.fn.tooltip.Constructor.Default.whiteList 6 | myDefaultWhiteList.textarea = []; 7 | myDefaultWhiteList.button = []; 8 | 9 | var $container = $(this); 10 | 11 | var titles = ['Very bad', 'Poor', 'OK', 'Good', 'Excellent']; 12 | 13 | for (var i = 0; i < 5; i++) { 14 | $container.append(``); 16 | } 17 | 18 | $stars = $container.children(); 19 | 20 | if ($container.hasClass('rating-faces')) { 21 | $stars.addClass('far fa-meh-blank'); 22 | } else if ($container.hasClass('empty-stars')) { 23 | $stars.addClass('far fa-star'); 24 | } else { 25 | $stars.addClass('fas fa-star'); 26 | } 27 | 28 | $stars.on('mouseover', function () { 29 | var index = $(this).attr('data-index'); 30 | markStarsAsActive(index); 31 | }); 32 | 33 | function markStarsAsActive(index) { 34 | unmarkActive(); 35 | 36 | for (var i = 0; i <= index; i++) { 37 | 38 | if ($container.hasClass('rating-faces')) { 39 | $($stars.get(i)).removeClass('fa-meh-blank'); 40 | $($stars.get(i)).addClass('live'); 41 | 42 | switch (index) { 43 | case '0': 44 | $($stars.get(i)).addClass('fa-angry'); 45 | break; 46 | case '1': 47 | $($stars.get(i)).addClass('fa-frown'); 48 | break; 49 | case '2': 50 | $($stars.get(i)).addClass('fa-meh'); 51 | break; 52 | case '3': 53 | $($stars.get(i)).addClass('fa-smile'); 54 | break; 55 | case '4': 56 | $($stars.get(i)).addClass('fa-laugh'); 57 | break; 58 | } 59 | 60 | } else if ($container.hasClass('empty-stars')) { 61 | $($stars.get(i)).addClass('fas'); 62 | switch (index) { 63 | case '0': 64 | $($stars.get(i)).addClass('oneStar'); 65 | break; 66 | case '1': 67 | $($stars.get(i)).addClass('twoStars'); 68 | break; 69 | case '2': 70 | $($stars.get(i)).addClass('threeStars'); 71 | break; 72 | case '3': 73 | $($stars.get(i)).addClass('fourStars'); 74 | break; 75 | case '4': 76 | $($stars.get(i)).addClass('fiveStars'); 77 | break; 78 | } 79 | } else { 80 | $($stars.get(i)).addClass('amber-text'); 81 | 82 | } 83 | } 84 | } 85 | 86 | function unmarkActive() { 87 | $stars.parent().hasClass('rating-faces') ? $stars.addClass('fa-meh-blank') : $stars; 88 | $container.hasClass('empty-stars') ? $stars.removeClass('fas') : $container; 89 | $stars.removeClass('fa-angry fa-frown fa-meh fa-smile fa-laugh live oneStar twoStars threeStars fourStars fiveStars amber-text'); 90 | } 91 | 92 | $stars.on('click', function () { 93 | $stars.popover('hide'); 94 | }); 95 | 96 | // Submit, you can add some extra custom code here 97 | // ex. to send the information to the server 98 | $container.on('click', '#voteSubmitButton', function () { 99 | $stars.popover('hide'); 100 | }); 101 | 102 | // Cancel, just close the popover 103 | $container.on('click', '#closePopoverButton', function () { 104 | $stars.popover('hide'); 105 | }); 106 | 107 | if ($container.hasClass('feedback')) { 108 | 109 | $(function () { 110 | $stars.popover({ 111 | // Append popover to #rateMe to allow handling form inside the popover 112 | container: $container, 113 | // Custom content for popover 114 | content: `
` 115 | }); 116 | }) 117 | } 118 | 119 | $stars.tooltip(); 120 | } 121 | })(jQuery); -------------------------------------------------------------------------------- /src/views/cart.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- include('./partials/head') %> 6 | 7 | 8 | 9 | <%- include('./partials/header') %> 10 |
11 |
12 |

Productos en mi carrito

13 |
14 |
15 | 16 | 17 | 18 | 21 | 24 | 27 | 30 | 33 | 34 | 35 | 36 | 37 | 38 |
19 | # 20 | 22 | Producto 23 | 25 | Precio 26 | 28 | Cantidad 29 | 31 | Subtotal 32 |
39 | 42 |
43 |
44 |
45 |
46 | Terminar la compra 47 |
48 |
49 |
50 |
51 | 52 | 60 |
61 | Elija un tipo de envio. 62 |
63 | 64 |
65 |
66 | 67 | 74 |
75 | Elija un tipo de envio. 76 |
77 | 78 |
79 |
80 | 82 |
83 |
84 |
85 |
86 | <%- include('./partials/footer') %> 87 | <%- include('./partials/scripts') %> 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /public/scss/addons/_datatables-select.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * MDBootstrap integration with Datatables 3 | * Learn more: https://mdbootstrap.com/docs/jquery/tables/datatables/ 4 | * About MDBootstrap: https://mdbootstrap.com/ 5 | * 6 | * This combined file was created taking that code from this webstie: 7 | * https://cdn.datatables.net/select/1.2.7/css/select.dataTables.min.css 8 | * 9 | * 10 | * To rebuild or modify this file with the latest versions of the included 11 | * software please visit: 12 | * https://datatables.net/download/#bs4/dt-1.10.18 13 | * 14 | * Included libraries: 15 | * DataTables 1.10.18 16 | */ 17 | 18 | table { 19 | &.dataTable { 20 | tbody > tr { 21 | &.selected, 22 | > .selected { 23 | background-color: #b0bed9; 24 | } 25 | } 26 | &.stripe, &.display { 27 | tbody > tr.odd { 28 | &.selected, 29 | > .selected { 30 | background-color: #acbad4; 31 | } 32 | } 33 | } 34 | &.hover, &.display { 35 | tbody > tr { 36 | &.selected:hover, 37 | > .selected:hover { 38 | background-color: #aab7d1; 39 | } 40 | } 41 | } 42 | &.order-column, &.display { 43 | tbody > tr { 44 | &.selected > { 45 | .sorting_1, .sorting_2, .sorting_3 { 46 | background-color: #acbad5; 47 | } 48 | } 49 | > .selected { 50 | background-color: #acbad5; 51 | } 52 | } 53 | } 54 | &.display, &.order-column.stripe { 55 | tbody > tr { 56 | &.odd.selected > .sorting_1 { 57 | background-color: #a6b4cd; 58 | } 59 | } 60 | } 61 | &.display tbody > tr.odd.selected > .sorting_2, &.order-column.stripe tbody > tr.odd.selected > .sorting_2 { 62 | background-color: #a8b5cf; 63 | } 64 | &.display tbody > tr.odd.selected > .sorting_3, &.order-column.stripe tbody > tr.odd.selected > .sorting_3 { 65 | background-color: #a9b7d1; 66 | } 67 | &.display tbody > tr.even.selected > .sorting_1, &.order-column.stripe tbody > tr.even.selected > .sorting_1 { 68 | background-color: #acbad5; 69 | } 70 | &.display tbody > tr.even.selected > .sorting_2, &.order-column.stripe tbody > tr.even.selected > .sorting_2 { 71 | background-color: #aebcd6; 72 | } 73 | &.display tbody > tr.even.selected > .sorting_3, &.order-column.stripe tbody > tr.even.selected > .sorting_3 { 74 | background-color: #afbdd8; 75 | } 76 | &.display tbody > tr.odd > .selected, &.order-column.stripe tbody > tr.odd > .selected { 77 | background-color: #a6b4cd; 78 | } 79 | &.display tbody > tr.even > .selected, &.order-column.stripe tbody > tr.even > .selected { 80 | background-color: #acbad5; 81 | } 82 | &.display tbody > tr.selected:hover > .sorting_1, &.order-column.hover tbody > tr.selected:hover > .sorting_1 { 83 | background-color: #a2aec7; 84 | } 85 | &.display tbody > tr.selected:hover > .sorting_2, &.order-column.hover tbody > tr.selected:hover > .sorting_2 { 86 | background-color: #a3b0c9; 87 | } 88 | &.display tbody > tr.selected:hover > .sorting_3, &.order-column.hover tbody > tr.selected:hover > .sorting_3 { 89 | background-color: #a5b2cb; 90 | } 91 | &.display, &.order-column.hover { 92 | tbody > tr { 93 | &:hover > .selected, > .selected:hover { 94 | background-color: #a2aec7; 95 | } 96 | } 97 | } 98 | tbody { 99 | td, th { 100 | &.select-checkbox { 101 | position: relative; 102 | &:before, 103 | &:after { 104 | position: absolute; 105 | top: 1.2em; 106 | left: 50%; 107 | box-sizing: border-box; 108 | display: block; 109 | width: 12px; 110 | height: 12px; 111 | } 112 | } 113 | } 114 | td.select-checkbox:before, 115 | th.select-checkbox:before { 116 | margin-top: 4px; 117 | margin-left: -6px; 118 | content: " "; 119 | border: 1px solid #000; 120 | border-radius: 3px; 121 | } 122 | } 123 | tr.selected { 124 | td.select-checkbox:after, 125 | th.select-checkbox:after { 126 | margin-top: 0; 127 | margin-left: -4px; 128 | text-align: center; 129 | text-shadow: 1px 1px #b0bed9, -1px -1px #b0bed9, 1px -1px #b0bed9, -1px 1px #b0bed9; 130 | content: "\2714"; 131 | } 132 | } 133 | } 134 | } 135 | 136 | div.dataTables_wrapper span { 137 | &.select-info, &.select-item { 138 | margin-left: .5em; 139 | } 140 | } 141 | 142 | @media screen and (max-width: 640px) { 143 | div.dataTables_wrapper span { 144 | &.select-info, &.select-item { 145 | display: block; 146 | margin-left: 0; 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /public/scss/free/_steppers.scss: -------------------------------------------------------------------------------- 1 | // Steppers 2 | ul.stepper { 3 | padding: 0 1.5rem; 4 | padding: 1.5rem; 5 | margin: 1em -1.5rem; 6 | overflow-x: hidden; 7 | overflow-y: auto; 8 | counter-reset: section; 9 | 10 | li { 11 | a { 12 | padding: $stepper-li-a-padding; 13 | text-align: center; 14 | 15 | .circle { 16 | display: inline-block; 17 | width: 1.75rem; 18 | height: 1.75rem; 19 | margin-right: $stepper-li-a-circle-mr; 20 | line-height: 1.7rem; 21 | color: $stepper-li-a-circle-color; 22 | text-align: center; 23 | background: $stepper-li-a-circle-bg; 24 | border-radius: $stepper-li-a-circle-border-radius; 25 | } 26 | 27 | .label { 28 | display: inline-block; 29 | color: $stepper-li-a-circle-bg; 30 | } 31 | } 32 | 33 | &.active, 34 | &.completed { 35 | a { 36 | .circle { 37 | @extend .primary-color; 38 | } 39 | 40 | .label { 41 | font-weight: 600; 42 | color: $stepper-li-a-label-color; 43 | } 44 | } 45 | } 46 | 47 | &.warning { 48 | a { 49 | .circle { 50 | // background-color: #ff3547 !important; 51 | @extend .danger-color !optional; 52 | } 53 | } 54 | } 55 | } 56 | } 57 | 58 | // Horizontal 59 | .stepper-horizontal { 60 | position: relative; 61 | display: flex; 62 | justify-content: space-between; 63 | 64 | li { 65 | position: relative; 66 | display: flex; 67 | flex: 1; 68 | align-items: center; 69 | transition: $stepper-horizontal-li-transition; 70 | 71 | a { 72 | .label { 73 | margin-top: $stepper-horizontal-li-a-label-mt; 74 | } 75 | } 76 | 77 | &:not(:last-child):after { 78 | position: relative; 79 | flex: 1; 80 | height: $stepper-horizontal-li-after-height; 81 | margin: $stepper-horizontal-li-after-margin 0 0 0; 82 | content: ""; 83 | background-color: $stepper-horizontal-li-after-bgc; 84 | } 85 | 86 | &:not(:first-child):before { 87 | position: relative; 88 | flex: 1; 89 | height: $stepper-horizontal-li-after-height; 90 | margin: $stepper-horizontal-li-after-margin 0 0 0; 91 | content: ""; 92 | background-color: $stepper-horizontal-li-after-bgc; 93 | } 94 | 95 | &:hover { 96 | background-color: rgba(0, 0, 0, .06); 97 | } 98 | } 99 | 100 | @media (max-width: $stepper-horizontal-breakpoint) { 101 | flex-direction: column; 102 | 103 | li { 104 | flex-direction: column; 105 | align-items: flex-start; 106 | 107 | a { 108 | .label { 109 | flex-flow: column nowrap; 110 | order: 2; 111 | margin-top: $stepper-horizontal-small-li-a-label-mt; 112 | } 113 | } 114 | 115 | &:not(:last-child):after { 116 | position: absolute; 117 | top: $stepper-horizontal-small-li-after-top; 118 | left: $stepper-horizontal-small-li-after-left; 119 | width: $stepper-horizontal-small-li-after-width; 120 | height: $stepper-horizontal-small-li-after-height; 121 | content: ""; 122 | } 123 | } 124 | } 125 | 126 | > li:not(:last-of-type) { 127 | margin-bottom: 0 !important; 128 | } 129 | } 130 | 131 | // Vertical 132 | .stepper-vertical { 133 | position: relative; 134 | display: flex; 135 | flex-direction: column; 136 | justify-content: space-between; 137 | 138 | li { 139 | position: relative; 140 | display: flex; 141 | flex: 1; 142 | flex-direction: column; 143 | align-items: flex-start; 144 | 145 | a { 146 | position: relative; 147 | display: flex; 148 | align-self: flex-start; 149 | 150 | .circle { 151 | order: 1; 152 | } 153 | 154 | .label { 155 | flex-flow: column nowrap; 156 | order: 2; 157 | margin-top: $stepper-vertical-li-a-label-mt; 158 | } 159 | } 160 | 161 | &.completed { 162 | a { 163 | .label { 164 | font-weight: 500; 165 | } 166 | } 167 | } 168 | 169 | .step-content { 170 | display: block; 171 | padding: $stepper-vertical-li-step-content-padding; 172 | margin-top: 0; 173 | margin-left: $stepper-vertical-li-step-content-ml; 174 | 175 | p { 176 | font-size: $stepper-vertical-li-step-content-p-font-size; 177 | } 178 | } 179 | 180 | &:not(:last-child):after { 181 | position: absolute; 182 | top: $stepper-vertical-li-after-top; 183 | left: $stepper-vertical-li-after-left; 184 | width: $stepper-vertical-li-after-width; 185 | height: $stepper-vertical-li-after-height; 186 | content: ""; 187 | background-color: $stepper-vertical-li-after-bgc; 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /public/scss/free/_treeview.scss: -------------------------------------------------------------------------------- 1 | // Treeview 2 | .treeview { 3 | &.w-20 { 4 | width: 20rem; 5 | } 6 | .rotate { 7 | margin-top: .2rem; 8 | font-size: .8rem; 9 | vertical-align: text-top; 10 | cursor: pointer; 11 | user-select: none; 12 | transition: all .1s linear; 13 | &.down { 14 | transform: rotate(90deg); 15 | } 16 | } 17 | .nested { 18 | display: none; 19 | } 20 | .active { 21 | display: block; 22 | } 23 | ul { 24 | list-style-type: none; 25 | } 26 | .ic-w { 27 | width: 1.3rem; 28 | } 29 | } 30 | 31 | .treeview-animated { 32 | &.w-20 { 33 | width: 20rem; 34 | } 35 | ul { 36 | position: relative; 37 | padding-left: 1em; 38 | list-style: none; 39 | } 40 | 41 | .treeview-animated-list { 42 | li { 43 | padding: .2em 0 0 .2em; 44 | } 45 | 46 | .treeview-animated-items { 47 | 48 | .nested { 49 | &::before { 50 | position: absolute; 51 | left: 5px; 52 | display: block; 53 | width: 5px; 54 | height: 100%; 55 | content: ""; 56 | background-color: #808080; 57 | } 58 | } 59 | 60 | .closed { 61 | display: block; 62 | padding: .2em .2em .2em .4em; 63 | margin-right: 0; 64 | border-top-left-radius: .3em; 65 | border-bottom-left-radius: .3em; 66 | 67 | &:hover { 68 | background-color: rgb(140, 185, 255); 69 | } 70 | 71 | .fa-angle-right { 72 | font-size: .8rem; 73 | transition: all .1s linear; 74 | 75 | &.down { 76 | position: relative; 77 | color: #f8f9fa; 78 | transform: rotate(90deg); 79 | } 80 | } 81 | } 82 | 83 | .open { 84 | background-color: rgb(50, 160, 255); 85 | transition: all .1s linear; 86 | 87 | &:hover { 88 | color: #f8f9fa; 89 | background-color: rgb(50, 160, 255); 90 | } 91 | 92 | span { 93 | color: #f8f9fa; 94 | } 95 | } 96 | } 97 | 98 | .treeview-animated-element { 99 | padding: .2em .2em .2em .6em; 100 | cursor: pointer; 101 | border-top-left-radius: 4px; 102 | border-bottom-left-radius: 4px; 103 | transition: all .1s linear; 104 | 105 | &:hover { 106 | background-color: rgb(140, 185, 255); 107 | } 108 | 109 | &.opened { 110 | color: #f8f9fa; 111 | background-color: rgb(50, 160, 255); 112 | 113 | &:hover { 114 | color: #f8f9fa; 115 | background-color: rgb(50, 160, 255); 116 | } 117 | } 118 | } 119 | } 120 | } 121 | 122 | .treeview-colorful { 123 | font-size: 16px; 124 | font-weight: 400; 125 | background: rgba(224, 127, 178, .2); 126 | 127 | &.w-20 { 128 | width: 20rem; 129 | } 130 | 131 | hr { 132 | border-color: #a2127a; 133 | } 134 | 135 | h6 { 136 | font-size: 1.4em; 137 | font-weight: 500; 138 | color: #a2127a; 139 | } 140 | 141 | ul { 142 | position: relative; 143 | padding-left: 0; 144 | list-style: none; 145 | } 146 | 147 | .treeview-colorful-list { 148 | 149 | ul { 150 | padding-left: 1em; 151 | margin-top: .1em; 152 | background: rgba(224, 127, 178, .2); 153 | } 154 | } 155 | 156 | .treeview-colorful-element { 157 | padding: .2em .2em .2em 1em; 158 | cursor: pointer; 159 | border: 2px solid transparent; 160 | border-right: 0 solid transparent; 161 | transition: all .1s linear; 162 | 163 | &:hover { 164 | background-color: #e07fb2; 165 | } 166 | 167 | &.opened { 168 | color: #ffac47; 169 | background-color: #a2127a; 170 | border: 2px solid #ffac47; 171 | border-right: 0 solid transparent; 172 | 173 | &:hover { 174 | color: #ffac47; 175 | background-color: #a2127a; 176 | } 177 | } 178 | 179 | } 180 | .treeview-colorful-items-header { 181 | display: block; 182 | padding: .4em; 183 | margin-right: 0; 184 | border-bottom: 2px solid transparent; 185 | transition: all .1s linear; 186 | 187 | &:hover { 188 | background-color: #e07fb2; 189 | } 190 | 191 | &.open { 192 | background-color: #a2127a; 193 | border-bottom: 2px solid #ffac47; 194 | transition: all .1s linear; 195 | 196 | span { 197 | color: #ffac47; 198 | } 199 | 200 | &:hover { 201 | color: #ffac47; 202 | background-color: #a2127a; 203 | } 204 | 205 | div:hover { 206 | background-color: #a2127a; 207 | } 208 | } 209 | 210 | .fa-angle-right { 211 | font-size: .8rem; 212 | transition: all .2s linear; 213 | } 214 | 215 | .fas { 216 | position: relative; 217 | color: #ffac47; 218 | transition: all .2s linear; 219 | transform: rotate(90deg); 220 | } 221 | 222 | .fa-minus-circle { 223 | position: relative; 224 | color: #ffac47; 225 | transition: all .2s linear; 226 | transform: rotate(180deg); 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/views/products/detail.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- include('../partials/head') %> 6 | 7 | 8 | 9 | 10 | <%- include('../partials/header') %> 11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 |
29 | 30 | 31 |
32 | 33 | 44 | 45 |

46 | 47 | $200 48 | 49 | $100 50 |

51 | 52 |

Description

53 | 54 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Et dolor suscipit libero eos 55 | atque quia ipsa 56 | sint voluptatibus! 57 | Beatae sit assumenda asperiores iure at maxime atque repellendus maiores quia 58 | sapiente.

59 | 60 |
61 | 62 | 64 | 67 | 68 |
69 | 70 |
71 | 72 | 73 |
74 | 75 | 76 |
77 | 78 | 79 |
80 | 81 | 82 |
83 | 84 | 85 |
86 | 87 |

Additional information

88 | 89 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Natus suscipit modi sapiente 90 | illo soluta odit 91 | voluptates, 92 | quibusdam officia. Neque quibusdam quas a quis porro? Molestias illo neque eum in 93 | laborum.

94 | 95 |
96 | 97 | 98 |
99 | 100 | 101 | 102 |
103 | 104 | 105 |
106 | 107 | 108 | 109 |
110 | 111 | 112 | 113 |
114 | 115 | 116 | 117 |
118 | 119 | 120 | 121 |
122 | 123 | 124 | 125 |
126 | 127 | 128 |
129 | 130 | 131 |
132 |
133 | 134 | <%- include('../partials/footer') %> 135 | <%- include('../partials/scripts') %> 136 | 137 | 138 | -------------------------------------------------------------------------------- /public/js/addons/imagesloaded.pkgd.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * imagesLoaded PACKAGED v4.1.4 3 | * JavaScript is all like "You images are done yet or what?" 4 | * MIT License 5 | */ 6 | 7 | !function(e,t){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",t):"object"==typeof module&&module.exports?module.exports=t():e.EvEmitter=t()}("undefined"!=typeof window?window:this,function(){function e(){}var t=e.prototype;return t.on=function(e,t){if(e&&t){var i=this._events=this._events||{},n=i[e]=i[e]||[];return n.indexOf(t)==-1&&n.push(t),this}},t.once=function(e,t){if(e&&t){this.on(e,t);var i=this._onceEvents=this._onceEvents||{},n=i[e]=i[e]||{};return n[t]=!0,this}},t.off=function(e,t){var i=this._events&&this._events[e];if(i&&i.length){var n=i.indexOf(t);return n!=-1&&i.splice(n,1),this}},t.emitEvent=function(e,t){var i=this._events&&this._events[e];if(i&&i.length){i=i.slice(0),t=t||[];for(var n=this._onceEvents&&this._onceEvents[e],o=0;o tr.selected, 18 | table.dataTable tbody > tr > .selected { 19 | background-color: #B0BED9; } 20 | 21 | table.dataTable.stripe tbody > tr.odd.selected, 22 | table.dataTable.stripe tbody > tr.odd > .selected, table.dataTable.display tbody > tr.odd.selected, 23 | table.dataTable.display tbody > tr.odd > .selected { 24 | background-color: #acbad4; } 25 | 26 | table.dataTable.hover tbody > tr.selected:hover, 27 | table.dataTable.hover tbody > tr > .selected:hover, table.dataTable.display tbody > tr.selected:hover, 28 | table.dataTable.display tbody > tr > .selected:hover { 29 | background-color: #aab7d1; } 30 | 31 | table.dataTable.order-column tbody > tr.selected > .sorting_1, table.dataTable.order-column tbody > tr.selected > .sorting_2, table.dataTable.order-column tbody > tr.selected > .sorting_3, table.dataTable.display tbody > tr.selected > .sorting_1, table.dataTable.display tbody > tr.selected > .sorting_2, table.dataTable.display tbody > tr.selected > .sorting_3 { 32 | background-color: #acbad5; } 33 | 34 | table.dataTable.order-column tbody > tr > .selected, table.dataTable.display tbody > tr > .selected { 35 | background-color: #acbad5; } 36 | 37 | table.dataTable.display tbody > tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody > tr.odd.selected > .sorting_1 { 38 | background-color: #a6b4cd; } 39 | 40 | table.dataTable.display tbody > tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody > tr.odd.selected > .sorting_2 { 41 | background-color: #a8b5cf; } 42 | 43 | table.dataTable.display tbody > tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody > tr.odd.selected > .sorting_3 { 44 | background-color: #a9b7d1; } 45 | 46 | table.dataTable.display tbody > tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody > tr.even.selected > .sorting_1 { 47 | background-color: #acbad5; } 48 | 49 | table.dataTable.display tbody > tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody > tr.even.selected > .sorting_2 { 50 | background-color: #aebcd6; } 51 | 52 | table.dataTable.display tbody > tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody > tr.even.selected > .sorting_3 { 53 | background-color: #afbdd8; } 54 | 55 | table.dataTable.display tbody > tr.odd > .selected, table.dataTable.order-column.stripe tbody > tr.odd > .selected { 56 | background-color: #a6b4cd; } 57 | 58 | table.dataTable.display tbody > tr.even > .selected, table.dataTable.order-column.stripe tbody > tr.even > .selected { 59 | background-color: #acbad5; } 60 | 61 | table.dataTable.display tbody > tr.selected:hover > .sorting_1, table.dataTable.order-column.hover tbody > tr.selected:hover > .sorting_1 { 62 | background-color: #a2aec7; } 63 | 64 | table.dataTable.display tbody > tr.selected:hover > .sorting_2, table.dataTable.order-column.hover tbody > tr.selected:hover > .sorting_2 { 65 | background-color: #a3b0c9; } 66 | 67 | table.dataTable.display tbody > tr.selected:hover > .sorting_3, table.dataTable.order-column.hover tbody > tr.selected:hover > .sorting_3 { 68 | background-color: #a5b2cb; } 69 | 70 | table.dataTable.display tbody > tr:hover > .selected, table.dataTable.display tbody > tr > .selected:hover, table.dataTable.order-column.hover tbody > tr:hover > .selected, table.dataTable.order-column.hover tbody > tr > .selected:hover { 71 | background-color: #a2aec7; } 72 | 73 | table.dataTable tbody td.select-checkbox, table.dataTable tbody th.select-checkbox { 74 | position: relative; } 75 | 76 | table.dataTable tbody td.select-checkbox:before, table.dataTable tbody td.select-checkbox:after, table.dataTable tbody th.select-checkbox:before, table.dataTable tbody th.select-checkbox:after { 77 | display: block; 78 | position: absolute; 79 | top: 1.2em; 80 | left: 50%; 81 | width: 12px; 82 | height: 12px; 83 | -webkit-box-sizing: border-box; 84 | box-sizing: border-box; } 85 | 86 | table.dataTable tbody td.select-checkbox:before, 87 | table.dataTable tbody th.select-checkbox:before { 88 | content: ' '; 89 | margin-top: 4px; 90 | margin-left: -6px; 91 | border: 1px solid black; 92 | -webkit-border-radius: 3px; 93 | border-radius: 3px; } 94 | 95 | table.dataTable tr.selected td.select-checkbox:after, 96 | table.dataTable tr.selected th.select-checkbox:after { 97 | content: '\2714'; 98 | margin-top: 0px; 99 | margin-left: -4px; 100 | text-align: center; 101 | text-shadow: 1px 1px #B0BED9, -1px -1px #B0BED9, 1px -1px #B0BED9, -1px 1px #B0BED9; } 102 | 103 | div.dataTables_wrapper span.select-info, div.dataTables_wrapper span.select-item { 104 | margin-left: 0.5em; } 105 | 106 | @media screen and (max-width: 640px) { 107 | div.dataTables_wrapper span.select-info, div.dataTables_wrapper span.select-item { 108 | margin-left: 0; 109 | display: block; } } 110 | -------------------------------------------------------------------------------- /src/views/partials/carousel.ejs: -------------------------------------------------------------------------------- 1 | 2 | 137 | -------------------------------------------------------------------------------- /public/js/modules/bs-custom-file-input.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * bsCustomFileInput v1.3.2 (https://github.com/Johann-S/bs-custom-file-input) 3 | * Copyright 2018 - 2019 Johann-S 4 | * Licensed under MIT (https://github.com/Johann-S/bs-custom-file-input/blob/master/LICENSE) 5 | */ 6 | (function (global, factory) { 7 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 8 | typeof define === 'function' && define.amd ? define(factory) : 9 | (global = global || self, global.bsCustomFileInput = factory()); 10 | }(this, function () { 11 | 'use strict'; 12 | 13 | var Selector = { 14 | CUSTOMFILE: '.custom-file input[type="file"]', 15 | CUSTOMFILELABEL: '.custom-file-label', 16 | FORM: 'form', 17 | INPUT: 'input' 18 | }; 19 | 20 | var textNodeType = 3; 21 | 22 | var getDefaultText = function getDefaultText(input) { 23 | var defaultText = ''; 24 | var label = input.parentNode.querySelector(Selector.CUSTOMFILELABEL); 25 | 26 | if (label) { 27 | defaultText = label.innerHTML; 28 | } 29 | 30 | return defaultText; 31 | }; 32 | 33 | var findFirstChildNode = function findFirstChildNode(element) { 34 | if (element.childNodes.length > 0) { 35 | var childNodes = [].slice.call(element.childNodes); 36 | 37 | for (var i = 0; i < childNodes.length; i++) { 38 | var node = childNodes[i]; 39 | 40 | if (node.nodeType !== textNodeType) { 41 | return node; 42 | } 43 | } 44 | } 45 | 46 | return element; 47 | }; 48 | 49 | var restoreDefaultText = function restoreDefaultText(input) { 50 | var defaultText = input.bsCustomFileInput.defaultText; 51 | var label = input.parentNode.querySelector(Selector.CUSTOMFILELABEL); 52 | 53 | if (label) { 54 | var element = findFirstChildNode(label); 55 | element.innerHTML = defaultText; 56 | } 57 | }; 58 | 59 | var fileApi = !!window.File; 60 | var FAKE_PATH = 'fakepath'; 61 | var FAKE_PATH_SEPARATOR = '\\'; 62 | 63 | var getSelectedFiles = function getSelectedFiles(input) { 64 | if (input.hasAttribute('multiple') && fileApi) { 65 | return [].slice.call(input.files).map(function (file) { 66 | return file.name; 67 | }).join(', '); 68 | } 69 | 70 | if (input.value.indexOf(FAKE_PATH) !== -1) { 71 | var splittedValue = input.value.split(FAKE_PATH_SEPARATOR); 72 | return splittedValue[splittedValue.length - 1]; 73 | } 74 | 75 | return input.value; 76 | }; 77 | 78 | function handleInputChange() { 79 | var label = this.parentNode.querySelector(Selector.CUSTOMFILELABEL); 80 | 81 | if (label) { 82 | var element = findFirstChildNode(label); 83 | var inputValue = getSelectedFiles(this); 84 | 85 | if (inputValue.length) { 86 | element.innerHTML = inputValue; 87 | } else { 88 | restoreDefaultText(this); 89 | } 90 | } 91 | } 92 | 93 | function handleFormReset() { 94 | var customFileList = [].slice.call(this.querySelectorAll(Selector.INPUT)).filter(function (input) { 95 | return !!input.bsCustomFileInput; 96 | }); 97 | 98 | for (var i = 0, len = customFileList.length; i < len; i++) { 99 | restoreDefaultText(customFileList[i]); 100 | } 101 | } 102 | 103 | var customProperty = 'bsCustomFileInput'; 104 | var Event = { 105 | FORMRESET: 'reset', 106 | INPUTCHANGE: 'change' 107 | }; 108 | var bsCustomFileInput = { 109 | init: function init(inputSelector, formSelector) { 110 | if (inputSelector === void 0) { 111 | inputSelector = Selector.CUSTOMFILE; 112 | } 113 | 114 | if (formSelector === void 0) { 115 | formSelector = Selector.FORM; 116 | } 117 | 118 | var customFileInputList = [].slice.call(document.querySelectorAll(inputSelector)); 119 | var formList = [].slice.call(document.querySelectorAll(formSelector)); 120 | 121 | for (var i = 0, len = customFileInputList.length; i < len; i++) { 122 | var input = customFileInputList[i]; 123 | Object.defineProperty(input, customProperty, { 124 | value: { 125 | defaultText: getDefaultText(input) 126 | }, 127 | writable: true 128 | }); 129 | handleInputChange.call(input); 130 | input.addEventListener(Event.INPUTCHANGE, handleInputChange); 131 | } 132 | 133 | for (var _i = 0, _len = formList.length; _i < _len; _i++) { 134 | formList[_i].addEventListener(Event.FORMRESET, handleFormReset); 135 | 136 | Object.defineProperty(formList[_i], customProperty, { 137 | value: true, 138 | writable: true 139 | }); 140 | } 141 | }, 142 | destroy: function destroy() { 143 | var formList = [].slice.call(document.querySelectorAll(Selector.FORM)).filter(function (form) { 144 | return !!form.bsCustomFileInput; 145 | }); 146 | var customFileInputList = [].slice.call(document.querySelectorAll(Selector.INPUT)).filter(function (input) { 147 | return !!input.bsCustomFileInput; 148 | }); 149 | 150 | for (var i = 0, len = customFileInputList.length; i < len; i++) { 151 | var input = customFileInputList[i]; 152 | restoreDefaultText(input); 153 | input[customProperty] = undefined; 154 | input.removeEventListener(Event.INPUTCHANGE, handleInputChange); 155 | } 156 | 157 | for (var _i2 = 0, _len2 = formList.length; _i2 < _len2; _i2++) { 158 | formList[_i2].removeEventListener(Event.FORMRESET, handleFormReset); 159 | 160 | formList[_i2][customProperty] = undefined; 161 | } 162 | } 163 | }; 164 | 165 | return bsCustomFileInput; 166 | 167 | })); 168 | //# sourceMappingURL=bs-custom-file-input.js.map 169 | 170 | document.addEventListener("DOMContentLoaded", function () { 171 | 172 | bsCustomFileInput.init() 173 | }); 174 | -------------------------------------------------------------------------------- /public/css/addons/datatables.css: -------------------------------------------------------------------------------- 1 | /* 2 | * MDBootstrap integration with Datatables 3 | * Learn more: https://mdbootstrap.com/docs/jquery/tables/datatables/ 4 | * About MDBootstrap: https://mdbootstrap.com/ 5 | * 6 | * This combined file was created by the DataTables downloader builder: 7 | * https://datatables.net/download 8 | * 9 | * To rebuild or modify this file with the latest versions of the included 10 | * software please visit: 11 | * https://datatables.net/download/#bs4/dt-1.10.18 12 | * 13 | * Included libraries: 14 | * DataTables 1.10.18 15 | */ 16 | table.dataTable thead { 17 | cursor: pointer; } 18 | table.dataTable thead > tr > th:active, 19 | table.dataTable thead > tr > td:active { 20 | outline: none; } 21 | 22 | div.dataTables_wrapper div.dataTables_length.d-flex.flex-row label { 23 | margin-top: 1.2rem; 24 | margin-right: 1rem; } 25 | 26 | div.dataTables_wrapper div.dataTables_length.d-flex.flex-row .select-wrapper.mdb-select span, 27 | div.dataTables_wrapper div.dataTables_length.d-flex.flex-row .select-wrapper.mdb-select .select-dropdown { 28 | margin-top: 1rem; } 29 | 30 | div.dataTables_wrapper div.dataTables_length label, div.dataTables_wrapper div.dataTables_filter label { 31 | text-align: left; 32 | font-weight: normal; 33 | padding-top: .5rem; 34 | padding-bottom: .5rem; } 35 | 36 | div.dataTables_wrapper div.dataTables_length select, 37 | div.dataTables_wrapper div.dataTables_length input { 38 | width: auto; } 39 | 40 | div.dataTables_wrapper div.dataTables_filter { 41 | text-align: right; } 42 | div.dataTables_wrapper div.dataTables_filter select, 43 | div.dataTables_wrapper div.dataTables_filter input { 44 | width: auto; } 45 | div.dataTables_wrapper div.dataTables_filter input { 46 | margin-left: .5rem; 47 | display: inline-block; } 48 | 49 | div.dataTables_wrapper div.dataTables_info, div.dataTables_wrapper div.dataTables_paginate { 50 | font-weight: normal; 51 | padding-top: 1rem; 52 | padding-bottom: 1rem; } 53 | 54 | div.dataTables_wrapper div.dataTables_paginate { 55 | text-align: right; 56 | margin: 0; } 57 | div.dataTables_wrapper div.dataTables_paginate ul.pagination { 58 | -webkit-box-pack: end; 59 | -webkit-justify-content: flex-end; 60 | -ms-flex-pack: end; 61 | justify-content: flex-end; } 62 | div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item.active .page-link:focus { 63 | background-color: #4285f4; } 64 | div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item .page-link:focus { 65 | -webkit-box-shadow: none; 66 | box-shadow: none; } 67 | 68 | @media (max-width: 767px) { 69 | div.dataTables_wrapper div .dataTables_length, 70 | div.dataTables_wrapper div .dataTables_filter, 71 | div.dataTables_wrapper div .dataTables_info, 72 | div.dataTables_wrapper div .dataTables_paginate ul.pagination { 73 | text-align: center; 74 | -webkit-box-pack: center; 75 | -webkit-justify-content: center; 76 | -ms-flex-pack: center; 77 | justify-content: center; } } 78 | 79 | .bs-select select { 80 | display: inline-block !important; } 81 | 82 | table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting, 83 | table.dataTable thead > tr > td.sorting_asc, 84 | table.dataTable thead > tr > td.sorting_desc, 85 | table.dataTable thead > tr > td.sorting { 86 | padding-right: 30px; } 87 | 88 | table.dataTable thead > tr > th:active, 89 | table.dataTable thead > tr > td:active { 90 | outline: none; } 91 | 92 | table.dataTable thead .sorting, 93 | table.dataTable thead .sorting_asc, 94 | table.dataTable thead .sorting_desc, 95 | table.dataTable thead .sorting_asc_disabled, 96 | table.dataTable thead .sorting_desc_disabled { 97 | cursor: pointer; 98 | position: relative; } 99 | 100 | table.dataTable thead .sorting:before, table.dataTable thead .sorting:after, 101 | table.dataTable thead .sorting_asc:before, 102 | table.dataTable thead .sorting_asc:after, 103 | table.dataTable thead .sorting_desc:before, 104 | table.dataTable thead .sorting_desc:after, 105 | table.dataTable thead .sorting_asc_disabled:before, 106 | table.dataTable thead .sorting_asc_disabled:after, 107 | table.dataTable thead .sorting_desc_disabled:before, 108 | table.dataTable thead .sorting_desc_disabled:after { 109 | position: absolute; 110 | bottom: 0.9em; 111 | display: block; 112 | opacity: 0.3; } 113 | 114 | table.dataTable thead .sorting:before, 115 | table.dataTable thead .sorting_asc:before, 116 | table.dataTable thead .sorting_desc:before, 117 | table.dataTable thead .sorting_asc_disabled:before, 118 | table.dataTable thead .sorting_desc_disabled:before { 119 | right: 1em; 120 | content: "\f0de"; } 121 | 122 | table.dataTable thead .sorting:after, 123 | table.dataTable thead .sorting_asc:after, 124 | table.dataTable thead .sorting_desc:after, 125 | table.dataTable thead .sorting_asc_disabled:after, 126 | table.dataTable thead .sorting_desc_disabled:after { 127 | content: "\f0dd"; 128 | right: 16px; } 129 | 130 | table.dataTable thead .sorting:before, 131 | table.dataTable thead .sorting_asc:before, 132 | table.dataTable thead .sorting_desc:before, 133 | table.dataTable thead .sorting_asc_disabled:before, 134 | table.dataTable thead .sorting_desc_disabled:before, 135 | table.dataTable thead .sorting:after, 136 | table.dataTable thead .sorting_asc:after, 137 | table.dataTable thead .sorting_desc:after, 138 | table.dataTable thead .sorting_asc_disabled:after, 139 | table.dataTable thead .sorting_desc_disabled:after { 140 | font-family: 'Font Awesome\ 5 Free'; 141 | font-weight: 900; 142 | font-size: 1rem; } 143 | 144 | table.dataTable thead .sorting_asc:before, 145 | table.dataTable thead .sorting_desc:after { 146 | opacity: 1; } 147 | 148 | table.dataTable thead .sorting_asc_disabled:before, 149 | table.dataTable thead .sorting_desc_disabled:after { 150 | opacity: 0; } 151 | --------------------------------------------------------------------------------