├── .gitignore ├── .htaccess ├── README.md ├── composer.json ├── composer.lock ├── dist ├── .htaccess ├── assets │ ├── js │ │ ├── app.js │ │ └── glider.min.js │ └── styles │ │ ├── glider.min.css │ │ ├── invoice.css │ │ └── main.css ├── img │ ├── basics.jpg │ ├── blog-1.jpg │ ├── blog-2.jpg │ ├── brand │ │ ├── favicon.png │ │ ├── imagotipo-b.png │ │ ├── imagotipo.png │ │ ├── isotipo-b.png │ │ ├── isotipo.png │ │ └── logotipo.png │ ├── header-main-pic.jpg │ ├── login.svg │ ├── newsletter-bg.jpg │ ├── pic-1.jpg │ ├── pic-2.jpg │ ├── pic-3.jpg │ ├── pic-4.jpg │ ├── product │ │ ├── 1000.jpg │ │ ├── 1001-b.jpg │ │ ├── 1001.jpg │ │ ├── 1002.jpg │ │ ├── 1003.jpg │ │ ├── 1004.jpg │ │ ├── 1005.jpg │ │ ├── 1006.jpg │ │ ├── 1007.jpg │ │ ├── 1008.jpg │ │ ├── 1009.jpg │ │ ├── 1010.jpg │ │ ├── 1011.jpg │ │ ├── 1012.jpg │ │ ├── 1013.jpg │ │ ├── 1014.jpg │ │ ├── 1015.jpg │ │ ├── 1016.jpg │ │ ├── 1017.jpg │ │ ├── 1018.jpg │ │ ├── 1019.jpg │ │ ├── 1020.jpg │ │ ├── 1021.jpg │ │ ├── 1022.jpg │ │ ├── 1023.jpg │ │ ├── 1024.jpg │ │ ├── 1025.jpg │ │ ├── 1026.jpg │ │ ├── 1027.jpg │ │ └── 1028.jpg │ └── roses-bg.jpg └── index.php ├── package-lock.json ├── package.json ├── resources ├── data │ └── shoppingcart.sql ├── sass │ ├── abstracts │ │ ├── _helpers.scss │ │ ├── _keyframes.scss │ │ ├── _mixins.scss │ │ └── _variables.scss │ ├── base │ │ ├── _settings.scss │ │ └── _typography.scss │ ├── components │ │ ├── _buttons.scss │ │ ├── _carousel.scss │ │ ├── _cart.scss │ │ ├── _notifications.scss │ │ └── _product_card.scss │ ├── layout │ │ ├── _banner.scss │ │ ├── _blog.scss │ │ ├── _cart_checkout_page.scss │ │ ├── _explore.scss │ │ ├── _footer.scss │ │ ├── _forgotpassword.scss │ │ ├── _header.scss │ │ ├── _navbar.scss │ │ ├── _newsletter.scss │ │ ├── _offers.scss │ │ ├── _product_details.scss │ │ ├── _profile.scss │ │ └── _profile_delete.scss │ ├── main.scss │ └── responsive │ │ ├── _1200.scss │ │ ├── _600.scss │ │ └── _900.scss ├── scripts │ ├── Carousels.js │ ├── CartUI.js │ ├── FormValidations.js │ ├── UserInterface.js │ └── index.js └── views │ ├── cache │ ├── 0204e23766bf00e917b686935a10700ff5958481.php │ ├── 0648c7784edd42fc40a3624191a789edf0253112.php │ ├── 0969d808bcb2e0a2eede2467d3a4ead6c1f94d98.php │ ├── 099e134fc735548ab5f6d93491acf24f96ae2eb0.php │ ├── 0bfef237117c3bb69bd89ec5a98669b206caaa4f.php │ ├── 0c21111f8b9af44f458f361442fbe3b37e88622a.php │ ├── 0ca8ac16aee9b5b042676f0a207bf3cbe304da9d.php │ ├── 14445286544f7e91b38f017d6f06d2241e0f676d.php │ ├── 16966b53754835fbc328c312b296e7243ec77f8a.php │ ├── 16cd276ae237d27157de8d33c399f9b0c7546d40.php │ ├── 195b0dbab34d9ee0769b13513082b723ca4cdb3c.php │ ├── 27f3eb99fed362cbd85ac92318f44efb4aaa4691.php │ ├── 2cbc33a03a083ea562a65bee0f3b9544b2a00125.php │ ├── 2d43cdf30e0985a951df00880517c2a3c363c073.php │ ├── 31326ef8ca9b14dca9f5af90b766a34152d9b378.php │ ├── 32803569cf21235b14c0db500429fbe7ca820bed.php │ ├── 33ac9faf019feb689133bf8f4dd54a490a08cf60.php │ ├── 3803d8e2f5fae6e82c09f22eff3a7ca495592413.php │ ├── 39d73d6082a0423c4f997a6bed2f82b0e13a5763.php │ ├── 4816d8fe5099c9b75bb6fbd6a698a220be7195f5.php │ ├── 48b46a5eeb7d0a98d777ce32263f438bd4d70113.php │ ├── 4a7f92372e04ed61575a919b9935054bf17df327.php │ ├── 510999beb41979dcc3381a53a73bb3e522575fc1.php │ ├── 599337bdb4ee7c5dbee7b2e49db9f3b96837f9ea.php │ ├── 5fb3cefc467fd86b663fe4e1251ea831039e187d.php │ ├── 623f8a09abdd2900be2989b87e16705bbee14d95.php │ ├── 6c59ac17e7cc293ad2f40255ea65fc53ba4abb88.php │ ├── 70bf6432eb73d4cf97f7be29cc4fe0eca08db832.php │ ├── 711c51d09ddb823cd38d10177e1417ac427ac567.php │ ├── 8472375c3b9707168d48d9dbb20ce227161b80ab.php │ ├── 8b06564c496026f8b05cf2de898387b4ecff7b56.php │ ├── 8c8167686d51e9cf5328d9973112d1446dc0e543.php │ ├── 93cc72e1b83b34ae1c90db6bd2b1e079ca05be5c.php │ ├── 94cee634e8542085c215ddc4034a52602f284443.php │ ├── 971a864b76d18014894e8c2e4dbf3f1156181c59.php │ ├── 9d044fdd0fce11d9cf481fcba8a8c3c0203af841.php │ ├── 9ee9e845dd7474f4f559469a3124162ebd3e65db.php │ ├── a1ee7fc894366d9237319847ad62065a42987d41.php │ ├── a2c70b0e494aea623a6534db7fe56473b813ec76.php │ ├── b13b75b834d09fc8150a4e8a2cf9876f5ba4bb59.php │ ├── b6aaf528f1dd0c642cf3fb7cf9db30a47d947d88.php │ ├── b6f14f80e72f4e9231202f0567443f9bbd33757f.php │ ├── b99e4da5cc02d998f62060bf64d3d5bc659ccf29.php │ ├── b9c807a5a14b50dd9ee4a0d99cd0df1e3f170bdf.php │ ├── bb968e55a5fc6f228e04184a2dddc86e4771a4a4.php │ ├── bef18315e1a7af5021e5346fb5590c3eee540630.php │ ├── c0c8ff197a01e9033b120ec99e4f0ff850163e38.php │ ├── c1a1768215f5223c70ded4301734da943800dae9.php │ ├── d537ca4801a544909c034202177a4ce0adef6746.php │ ├── e14df9a7db001daf89855806d947d971b0762f3a.php │ ├── e4f26c89cefc8684a0af1ecddfa6f07b81c98195.php │ ├── ebf8c0e064599a125f34f81f7daf0e823b065fd0.php │ ├── ec95b9867a30b795d3e69a21c4c40933b72c17f7.php │ ├── ed137a898b4abcf698c0e2452e30db55de0a319d.php │ ├── ed74b4d11cda39896da6ffff8afa7214c4d32af2.php │ ├── f72eb3562f434107b18b658536c8ce6208632811.php │ ├── f83accc77e0be1ad8f493a38313718546a529425.php │ └── feb2ed127f60f3c03e441d329fdaa2f6b3ff31b5.php │ ├── components │ ├── login_form.blade.php │ ├── notification.blade.php │ ├── product_card.blade.php │ └── register_form.blade.php │ ├── email_templates │ ├── resetpass_html.blade.php │ └── resetpass_txt.blade.php │ ├── layouts │ ├── cart_checkout_page.blade.php │ ├── explore.blade.php │ ├── forget_password.blade.php │ ├── home.blade.php │ ├── invoice.blade.php │ ├── product_details.blade.php │ ├── profile.blade.php │ ├── profile_delete.blade.php │ └── reset_password.blade.php │ └── sections │ ├── banner.blade.php │ ├── best_sellers.blade.php │ ├── blog.blade.php │ ├── footer.blade.php │ ├── header.blade.php │ ├── items_w_discount.blade.php │ ├── navbar.blade.php │ ├── newsletter.blade.php │ └── offers.blade.php ├── src ├── Config │ ├── constants.php │ ├── routes.php │ └── utilities.php ├── Controllers │ ├── Account │ │ ├── Accounts.php │ │ ├── Auth.php │ │ └── Passwords.php │ ├── Checkout │ │ ├── Orders.php │ │ └── Payments.php │ ├── Helpers │ │ ├── Cookies.php │ │ ├── Flash.php │ │ ├── Mail.php │ │ ├── PDF.php │ │ ├── Token.php │ │ └── Validations.php │ ├── Merchandise │ │ ├── CartOperations.php │ │ └── Products.php │ └── ViewLoaders.php ├── Core │ ├── Database.php │ ├── Router.php │ └── View.php └── models │ ├── Authentication │ ├── Password.php │ ├── Session.php │ └── User.php │ ├── Checkout │ └── Order.php │ └── Merchandise │ ├── Cart.php │ └── Product.php └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | node_modules 3 | .phpintel 4 | .env -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine on 3 | RewriteRule ^$ dist/ [L] 4 | RewriteRule (.*) dist/$1 [L] 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🛒 Ecommerce site built using pure PHP & vanilla JS 2 | 3 | This project is an ecommerce wesbite built without any frameworks. My goal was to learn how to code a functional application to learn fundamentals of OOP and the MVC architecture while setting up a somewhat modern development environment. 4 | 5 | ## ⚒️ This project was built using: 6 | 7 | - PHP7 8 | - JavaScript ES6 9 | - Composer packages like: 10 | - philo/laravel-blade as the template engine 11 | - PHPMailer: to send emails 12 | - Omnipay to handle payments through PayPal API 13 | - Webpack & Babel 14 | - Sass 15 | 16 | ## ⚙️Installation 17 | 18 | If you want to test this project on your local machine, you have to follow these steps: 19 | 20 | 1. **Download the code** 21 | You can either download the .zip file or run `git clone https://github.com/crjoseabraham/ecommerce-NoFramework.git` 22 | 23 | 2. **Install dependencies** 24 | Then, install all dependencies by running: 25 | `npm install` and `composer install` 26 | 27 | 3. **Import database** 28 | Go to your database manager and import the .sql file located in `resources/data/`. The `CREATE DATABASE` command is included already. 29 | 30 | 4. **Update environment variables** 31 | Create a `.env` file and set the following variables: 32 | 33 | ``` 34 | URLROOT 35 | SECRET_KEY 36 | ------------------ 37 | DB_HOST 38 | DB_USER 39 | DB_PASS 40 | DB_NAME 41 | ------------------ 42 | BRAND_EMAIL 43 | BRAND_EMAIL_PASS 44 | ------------------ 45 | PAYPAL_EMAIL 46 | PAYPAL_USERNAME 47 | PAYPAL_PASSWORD 48 | PAYPAL_SIGNATURE 49 | PAYPAL_CLIENT_ID 50 | PAYPAL_SECRET 51 | ``` 52 | 53 | And change the project base URL in [resources/scripts/CartUI.js](https://github.com/crjoseabraham/ecommerce-NoFramework/blob/master/resources/scripts/CartUI.js) which is set up for localhost 54 | **(lines 77 and 116)**. 55 | 56 | An small thing you may want to change or even delete is the timezone I set in [public/index.php](https://github.com/crjoseabraham/ecommerce-NoFramework/blob/master/dist/index.php) because I used my timezone 57 | 58 | `date_default_timezone_set('America/Caracas');` 59 | 60 | ## 🏆 Goals achieved 61 | 62 | - [x] Learn the principles of OOP and how to apply the MVC architecture 63 | - [x] How to work with sessions and cookies in PHP 64 | - [x] Implement user authentication (logging in, logging out, remember session, recover passwords, etc.). 65 | - [x] Work with third party libraries 66 | - [x] Practice modern JavaScript and transpile it using Webpack and Babel 67 | - [x] Basics of version control with Git (work with repos, commits, branching and merging). 68 | - [x] Set up PayPal sandbox 69 | 70 | ### Preview 71 | 72 | ![Project Preview](https://i.imgur.com/XA35ltf.jpg) 73 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "joseabraham/ecommerce", 3 | "description": "Ecommerce site with pure PHP and vanilla JavaScript", 4 | "type": "project", 5 | "authors": [ 6 | { 7 | "name": "Jose Abraham Castillo", 8 | "email": "crjoseabraham@gmail.com" 9 | } 10 | ], 11 | "autoload": { 12 | "files": [ 13 | "src/Config/constants.php", 14 | "src/Config/utilities.php" 15 | ], 16 | "psr-4": { 17 | "App\\Core\\": "src/Core", 18 | "App\\Controller\\": "src/Controllers", 19 | "App\\Controller\\Account\\": "src/Controllers/Account", 20 | "App\\Controller\\Checkout\\": "src/Controllers/Checkout", 21 | "App\\Controller\\Helper\\": "src/Controllers/Helpers", 22 | "App\\Controller\\Merchandise\\": "src/Controllers/Merchandise", 23 | "App\\Model\\": "src/Models", 24 | "App\\Model\\Authentication\\": "src/Models/Authentication", 25 | "App\\Model\\Merchandise\\": "src/Models/Merchandise" 26 | } 27 | }, 28 | "require": { 29 | "philo/laravel-blade": "^3.1", 30 | "phpmailer/phpmailer": "^6.1", 31 | "vlucas/phpdotenv": "^5.2", 32 | "league/omnipay": "^3.0", 33 | "omnipay/paypal": "^3.0", 34 | "mpdf/mpdf": "^8.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /dist/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | Options -Multiviews 3 | RewriteEngine On 4 | RewriteBase /shoppingcart/dist 5 | RewriteCond %{REQUEST_FILENAME} !-d 6 | RewriteCond %{REQUEST_FILENAME} !-f 7 | RewriteRule ^(.+)$ index.php?url=$1 [QSA,L] 8 | -------------------------------------------------------------------------------- /dist/assets/styles/glider.min.css: -------------------------------------------------------------------------------- 1 | .glider,.glider-contain{margin:0 auto;position:relative}.glider,.glider-track{transform:translateZ(0)}.glider-dot,.glider-next,.glider-prev{border:0;padding:0;user-select:none;outline:0}.glider-contain{width:100%}.glider{overflow-y:hidden;-webkit-overflow-scrolling:touch;-ms-overflow-style:none}.glider-track{width:100%;margin:0;padding:0;display:flex;z-index:1}.glider.draggable{user-select:none;cursor:-webkit-grab;cursor:grab}.glider.draggable .glider-slide img{user-select:none;pointer-events:none}.glider.drag{cursor:-webkit-grabbing;cursor:grabbing}.glider-slide{user-select:none;justify-content:center;align-content:center;width:100%;min-width:150px}.glider-slide img{max-width:100%}.glider::-webkit-scrollbar{opacity:0;height:0}.glider-next,.glider-prev{position:absolute;background:0 0;z-index:2;font-size:40px;text-decoration:none;left:-23px;top:30%;cursor:pointer;color:#666;opacity:1;line-height:1;transition:opacity .5s cubic-bezier(.17,.67,.83,.67),color .5s cubic-bezier(.17,.67,.83,.67)}.glider-next:focus,.glider-next:hover,.glider-prev:focus,.glider-prev:hover{color:#ccc}.glider-next{right:-23px;left:auto}.glider-next.disabled,.glider-prev.disabled{opacity:.25;color:#666;cursor:default}.glider-hide{opacity:0}.glider-dots{user-select:none;display:flex;flex-wrap:wrap;justify-content:center;margin:0 auto;padding:0}.glider-dot{display:block;cursor:pointer;color:#ccc;border-radius:999px;background:#ccc;width:12px;height:12px;margin:7px}.glider-dot:focus,.glider-dot:hover{background:#ddd}.glider-dot.active{background:#a89cc8}@media(max-width:36em){.glider::-webkit-scrollbar{opacity:1;-webkit-appearance:none;width:7px;height:3px}.glider::-webkit-scrollbar-thumb{opacity:1;border-radius:99px;background-color:rgba(156,156,156,.25);-webkit-box-shadow:0 0 1px rgba(255,255,255,.25);box-shadow:0 0 1px rgba(255,255,255,.25)}} 2 | -------------------------------------------------------------------------------- /dist/assets/styles/invoice.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::after, 3 | *::before { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | } 8 | body { 9 | background-image: linear-gradient(to right top, #fff, #fed1bd); 10 | } 11 | table { 12 | width: 100%; 13 | } 14 | /* HEADER */ 15 | .header table { 16 | width: 100%; 17 | } 18 | .header table td { 19 | padding: 1em 0; 20 | } 21 | .brand-logo { 22 | max-width: 120px; 23 | } 24 | /* CONTENT */ 25 | .content { 26 | background-color: #eeeeee; 27 | border: 1px solid #312c26; 28 | padding: 1em 2em; 29 | } 30 | h1 { 31 | font-family: serif; 32 | font-size: 40px; 33 | text-align: center; 34 | font-style: italic; 35 | } 36 | 37 | .content p { 38 | font-size: 16px; 39 | line-height: 1; 40 | } 41 | .content p strong { 42 | padding-right: 10px; 43 | } 44 | 45 | /* Table of items */ 46 | table.purchases { 47 | font-family: sans-serif; 48 | width: 100%; 49 | border-radius: 5px; 50 | border-collapse: collapse; 51 | border-style: hidden; 52 | box-shadow: 0 0 0 1px #ccc; 53 | } 54 | table.purchases td { 55 | vertical-align: top; 56 | border-left: 0.1mm solid #bbb; 57 | border-right: 0.1mm solid #bbb; 58 | } 59 | table.purchases thead td { 60 | background-color: #dddddd; 61 | text-align: center; 62 | border: 0.1mm solid #bbb; 63 | font-variant: small-caps; 64 | } 65 | table.purchases td.blanktotal { 66 | border: 0.1mm solid #bbb; 67 | border: 0mm none #bbb; 68 | border-top: 0.1mm solid #bbb; 69 | border-right: 0.1mm solid #bbb; 70 | } 71 | table.purchases td.totals { 72 | text-align: right; 73 | border: 0.1mm solid #bbb; 74 | } 75 | -------------------------------------------------------------------------------- /dist/img/basics.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/basics.jpg -------------------------------------------------------------------------------- /dist/img/blog-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/blog-1.jpg -------------------------------------------------------------------------------- /dist/img/blog-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/blog-2.jpg -------------------------------------------------------------------------------- /dist/img/brand/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/brand/favicon.png -------------------------------------------------------------------------------- /dist/img/brand/imagotipo-b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/brand/imagotipo-b.png -------------------------------------------------------------------------------- /dist/img/brand/imagotipo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/brand/imagotipo.png -------------------------------------------------------------------------------- /dist/img/brand/isotipo-b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/brand/isotipo-b.png -------------------------------------------------------------------------------- /dist/img/brand/isotipo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/brand/isotipo.png -------------------------------------------------------------------------------- /dist/img/brand/logotipo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/brand/logotipo.png -------------------------------------------------------------------------------- /dist/img/header-main-pic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/header-main-pic.jpg -------------------------------------------------------------------------------- /dist/img/newsletter-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/newsletter-bg.jpg -------------------------------------------------------------------------------- /dist/img/pic-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/pic-1.jpg -------------------------------------------------------------------------------- /dist/img/pic-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/pic-2.jpg -------------------------------------------------------------------------------- /dist/img/pic-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/pic-3.jpg -------------------------------------------------------------------------------- /dist/img/pic-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/pic-4.jpg -------------------------------------------------------------------------------- /dist/img/product/1000.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1000.jpg -------------------------------------------------------------------------------- /dist/img/product/1001-b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1001-b.jpg -------------------------------------------------------------------------------- /dist/img/product/1001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1001.jpg -------------------------------------------------------------------------------- /dist/img/product/1002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1002.jpg -------------------------------------------------------------------------------- /dist/img/product/1003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1003.jpg -------------------------------------------------------------------------------- /dist/img/product/1004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1004.jpg -------------------------------------------------------------------------------- /dist/img/product/1005.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1005.jpg -------------------------------------------------------------------------------- /dist/img/product/1006.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1006.jpg -------------------------------------------------------------------------------- /dist/img/product/1007.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1007.jpg -------------------------------------------------------------------------------- /dist/img/product/1008.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1008.jpg -------------------------------------------------------------------------------- /dist/img/product/1009.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1009.jpg -------------------------------------------------------------------------------- /dist/img/product/1010.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1010.jpg -------------------------------------------------------------------------------- /dist/img/product/1011.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1011.jpg -------------------------------------------------------------------------------- /dist/img/product/1012.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1012.jpg -------------------------------------------------------------------------------- /dist/img/product/1013.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1013.jpg -------------------------------------------------------------------------------- /dist/img/product/1014.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1014.jpg -------------------------------------------------------------------------------- /dist/img/product/1015.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1015.jpg -------------------------------------------------------------------------------- /dist/img/product/1016.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1016.jpg -------------------------------------------------------------------------------- /dist/img/product/1017.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1017.jpg -------------------------------------------------------------------------------- /dist/img/product/1018.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1018.jpg -------------------------------------------------------------------------------- /dist/img/product/1019.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1019.jpg -------------------------------------------------------------------------------- /dist/img/product/1020.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1020.jpg -------------------------------------------------------------------------------- /dist/img/product/1021.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1021.jpg -------------------------------------------------------------------------------- /dist/img/product/1022.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1022.jpg -------------------------------------------------------------------------------- /dist/img/product/1023.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1023.jpg -------------------------------------------------------------------------------- /dist/img/product/1024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1024.jpg -------------------------------------------------------------------------------- /dist/img/product/1025.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1025.jpg -------------------------------------------------------------------------------- /dist/img/product/1026.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1026.jpg -------------------------------------------------------------------------------- /dist/img/product/1027.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1027.jpg -------------------------------------------------------------------------------- /dist/img/product/1028.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/product/1028.jpg -------------------------------------------------------------------------------- /dist/img/roses-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crjoseabraham/php-ecommerce/95d9fb96f03ef609daba0dd5e9df353aef510414/dist/img/roses-bg.jpg -------------------------------------------------------------------------------- /dist/index.php: -------------------------------------------------------------------------------- 1 | load(); 16 | 17 | App\Core\Router::loadRoutes(dirname(__DIR__) . '/src/Config/routes.php')->redirect(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecommerce", 3 | "version": "2.0.1", 4 | "description": "\"About the fit\" - Ecommerce site with PHP and modern JavaScript", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "webpack --watch --mode=development", 8 | "build": "webpack --mode=production" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/crjoseabraham/ecommerce-NoFramework.git" 13 | }, 14 | "keywords": [ 15 | "ecommerce", 16 | "php", 17 | "php7", 18 | "shoppingcart", 19 | "mvc" 20 | ], 21 | "author": "Jose Abraham Castillo", 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/crjoseabraham/ecommerce-NoFramework/issues" 25 | }, 26 | "homepage": "https://github.com/crjoseabraham/ecommerce-NoFramework#readme", 27 | "devDependencies": { 28 | "@babel/core": "^7.9.0", 29 | "@babel/preset-env": "^7.9.0", 30 | "autoprefixer": "^9.7.5", 31 | "babel-loader": "^8.1.0", 32 | "css-loader": "^3.4.2", 33 | "file-loader": "^6.0.0", 34 | "image-webpack-loader": "^6.0.0", 35 | "mini-css-extract-plugin": "^0.9.0", 36 | "node-sass": "^7.0.0", 37 | "postcss-loader": "^3.0.0", 38 | "sass-loader": "^8.0.2", 39 | "style-loader": "^1.1.3", 40 | "webpack": "^4.42.1", 41 | "webpack-cli": "^3.3.11" 42 | }, 43 | "dependencies": { 44 | "babel-polyfill": "^6.26.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /resources/sass/abstracts/_helpers.scss: -------------------------------------------------------------------------------- 1 | // DIVs 2 | .container { 3 | margin: auto; 4 | width: 90%; 5 | max-width: 1200px; 6 | } 7 | 8 | // overlay 9 | #overlay { 10 | position: absolute; 11 | top: 0; 12 | left: 0; 13 | z-index: -100; 14 | width: 100%; 15 | height: 100%; 16 | background: rgba(0, 0, 0, 0.4); 17 | opacity: 0; 18 | 19 | &.active { 20 | z-index: 995; 21 | opacity: 1; 22 | } 23 | } 24 | 25 | // Images 26 | .cover { 27 | background-size: cover; 28 | } 29 | 30 | // Whitespacing 31 | .mt-2 { 32 | margin-top: 2rem; 33 | } 34 | .mt-4 { 35 | margin-top: 4rem; 36 | } 37 | .ml-2 { 38 | margin-left: 2rem; 39 | } 40 | 41 | .pv2 { 42 | padding-top: 2rem; 43 | padding-bottom: 2rem; 44 | } 45 | .ph2 { 46 | padding-left: 2rem; 47 | padding-right: 2rem; 48 | } 49 | .pv4 { 50 | padding-top: 4rem; 51 | padding-bottom: 4rem; 52 | } 53 | .ph4 { 54 | padding-left: 4rem; 55 | padding-right: 4rem; 56 | } 57 | .pv8 { 58 | padding-top: 8rem; 59 | padding-bottom: 8rem; 60 | } 61 | .ph8 { 62 | padding-left: 8rem; 63 | padding-right: 8rem; 64 | } 65 | 66 | // No Scroll for body 67 | .noscroll { 68 | overflow: hidden; 69 | } 70 | 71 | .blur > nav, 72 | .blur > header, 73 | .blur > section, 74 | .blur > footer { 75 | filter: blur(5px); 76 | } 77 | 78 | // Inline elements 79 | .inline-sb { 80 | display: flex; 81 | justify-content: space-between; 82 | align-items: center; 83 | width: 100%; 84 | } 85 | 86 | // Color change 87 | .tulip { 88 | color: $tulip; 89 | } 90 | 91 | .grullo { 92 | color: $grullo; 93 | } 94 | 95 | .red { 96 | color: #ff0000; 97 | } 98 | 99 | .black { 100 | color: #000 !important; 101 | } 102 | 103 | .transparent { 104 | background: transparent !important; 105 | } 106 | -------------------------------------------------------------------------------- /resources/sass/abstracts/_keyframes.scss: -------------------------------------------------------------------------------- 1 | @keyframes scaleImg { 2 | from { 3 | transform: scale(0); 4 | } 5 | to { 6 | transform: scale(1); 7 | } 8 | } 9 | 10 | @keyframes fadeRight { 11 | 0% { 12 | display: block; 13 | transform: translateX(100%); 14 | opacity: 0; 15 | } 16 | 10% { 17 | transform: translateX(0); 18 | opacity: 1; 19 | } 20 | 90% { 21 | transform: translateX(0); 22 | opacity: 1; 23 | } 24 | 100% { 25 | transform: translateX(100%); 26 | opacity: 0; 27 | display: none; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /resources/sass/abstracts/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin headeroverlay($opacity) { 2 | &::before { 3 | content: ""; 4 | position: absolute; 5 | top: 0; 6 | left: 0; 7 | right: 0; 8 | bottom: 0; 9 | background-image: linear-gradient(to bottom right, $eggshell, $peach); 10 | opacity: $opacity; 11 | } 12 | } 13 | 14 | @mixin btn-hover-effect($width, $time, $opacity) { 15 | overflow: hidden; 16 | position: relative; 17 | 18 | &::after { 19 | background: $white; 20 | content: ""; 21 | height: 155px; 22 | left: -150px; 23 | opacity: $opacity; 24 | position: absolute; 25 | top: -50px; 26 | transform: rotate(35deg); 27 | transition: all $time cubic-bezier(0.19, 1, 0.22, 1); 28 | width: $width; 29 | z-index: 1; 30 | } 31 | 32 | &:hover::after { 33 | left: 120%; 34 | transition: all $time cubic-bezier(0.19, 1, 0.22, 1); 35 | } 36 | } 37 | 38 | @mixin link-hover-effect($bg-color) { 39 | &::before { 40 | content: ""; 41 | height: 1px; 42 | width: 100%; 43 | background: $bg-color; 44 | position: absolute; 45 | bottom: -2px; 46 | left: 0; 47 | transition: all 0.3s ease; 48 | transform: scaleX(0); 49 | transform-origin: left; 50 | } 51 | 52 | &:hover { 53 | color: $grullo; 54 | 55 | &::before { 56 | background: $grullo; 57 | transform: scaleX(1); 58 | transform-origin: right; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /resources/sass/abstracts/_variables.scss: -------------------------------------------------------------------------------- 1 | $heading: "Playfair Display", serif; 2 | $body: "Work Sans", sans-serif; 3 | 4 | // Colors 5 | $bistre: #312c26; 6 | $grullo: #b2a08a; 7 | $eggshell: #f2e9db; 8 | $white: #ffffff; 9 | $peach: #fed1bd; 10 | $tulip: #fc8b93; 11 | $green: #00c853; 12 | $yellow: #ffd640; 13 | -------------------------------------------------------------------------------- /resources/sass/base/_typography.scss: -------------------------------------------------------------------------------- 1 | .fs16 { 2 | font-size: 1.6rem; 3 | } 4 | .fs18 { 5 | font-size: 1.8rem; 6 | } 7 | .fs20 { 8 | font-size: 2rem; 9 | } 10 | .fs24 { 11 | font-size: 2.4rem; 12 | } 13 | .fs32 { 14 | font-size: 3.2rem; 15 | } 16 | 17 | .serif { 18 | font-family: $heading; 19 | } 20 | 21 | .sans { 22 | font-family: $body; 23 | } 24 | 25 | .italic { 26 | font-style: italic; 27 | } 28 | 29 | .upper { 30 | text-transform: uppercase; 31 | } 32 | 33 | .stroked { 34 | text-decoration: line-through; 35 | } 36 | 37 | .center { 38 | text-align: center; 39 | } 40 | 41 | .right { 42 | text-align: right; 43 | } 44 | -------------------------------------------------------------------------------- /resources/sass/components/_buttons.scss: -------------------------------------------------------------------------------- 1 | button { 2 | cursor: pointer; 3 | } 4 | 5 | .btn { 6 | display: inline-block; 7 | padding: 1rem 2rem; 8 | font-family: $body; 9 | font-size: 1.5rem; 10 | text-transform: uppercase; 11 | transition: all 0.3s ease; 12 | cursor: pointer; 13 | 14 | &--icon { 15 | font-size: 2rem; 16 | color: $bistre; 17 | padding: 0; 18 | background: transparent; 19 | border: none; 20 | margin-left: 3rem; 21 | transition: all 0.3s ease; 22 | @include link-hover-effect($bistre); 23 | 24 | &::before { 25 | display: none; 26 | } 27 | } 28 | 29 | &--blank { 30 | color: $white; 31 | border: 1px solid currentColor; 32 | padding: 1.3rem 2rem; 33 | @include btn-hover-effect(50px, 550ms, 0.4); 34 | } 35 | 36 | &--mini { 37 | padding: 0; 38 | font-size: 1.4rem; 39 | background: transparent; 40 | border: none; 41 | text-transform: none; 42 | color: $tulip; 43 | position: relative; 44 | 45 | @include link-hover-effect($tulip); 46 | } 47 | 48 | &--link { 49 | padding: 0; 50 | color: lighten($bistre, 40%); 51 | border: none; 52 | background: transparent; 53 | } 54 | 55 | &--simple { 56 | background: transparent; 57 | border: none; 58 | padding: 0; 59 | font-size: 1.6rem; 60 | position: relative; 61 | @include link-hover-effect($bistre); 62 | } 63 | 64 | &--primary { 65 | padding: 1.3rem 2rem; 66 | font-size: 1.6rem; 67 | font-weight: 500; 68 | border: none; 69 | color: $white; 70 | background: #000; 71 | @include btn-hover-effect(90px, 500ms, 0.8); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /resources/sass/components/_carousel.scss: -------------------------------------------------------------------------------- 1 | .glider-contain.single-item, 2 | .glider-contain.single-item .product__card.glider-slide { 3 | width: 29.5rem; 4 | } 5 | 6 | .glider-track { 7 | scrollbar-width: none; 8 | } 9 | 10 | .glider-slide { 11 | margin: 0 0.25rem; 12 | 13 | img { 14 | display: block; 15 | } 16 | } 17 | 18 | .glider-next, 19 | .glider-next:focus, 20 | .glider-prev, 21 | .glider-prev:focus { 22 | color: $tulip; 23 | } 24 | 25 | .glider-next, 26 | .glider-prev { 27 | background-color: rgba(255, 255, 255, 0.3); 28 | font-size: 4.3rem; 29 | text-transform: uppercase; 30 | top: 40%; 31 | border-radius: 100%; 32 | padding: 0.6rem 1.214rem; 33 | 34 | &:hover { 35 | background-color: rgba(255, 255, 255, 0.6); 36 | color: darken($tulip, 17%); 37 | } 38 | } 39 | 40 | .glider-prev { 41 | left: 1rem; 42 | 43 | i { 44 | margin-right: 2px; 45 | } 46 | } 47 | 48 | .glider-next { 49 | right: 1rem; 50 | 51 | i { 52 | margin-left: 2px; 53 | } 54 | } 55 | 56 | @-moz-document url-prefix() { 57 | .glider-track { 58 | margin-bottom: 17px; 59 | } 60 | .glider-wrap { 61 | overflow: hidden; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /resources/sass/components/_notifications.scss: -------------------------------------------------------------------------------- 1 | .notification-container { 2 | width: 380px; 3 | position: fixed; 4 | top: 9rem; 5 | right: 5.5%; 6 | z-index: 9999; 7 | display: flex; 8 | flex-direction: column; 9 | } 10 | 11 | .notification { 12 | font-family: $body; 13 | width: 350px; 14 | box-shadow: 0px 1px 10px #ccc; 15 | border-radius: 4px; 16 | background: rgba(255, 255, 255, 0.95); 17 | backdrop-filter: blur(5px); 18 | padding: 1.5rem 3rem; 19 | animation: fadeRight 5s; 20 | animation-fill-mode: forwards; 21 | 22 | &[data-type="success"]::before { 23 | border: 3px solid $green; 24 | } 25 | &[data-type="info"]::before { 26 | border: 3px solid $yellow; 27 | } 28 | &[data-type="danger"]::before { 29 | border: 3px solid darken($tulip, 15%); 30 | } 31 | 32 | &::before { 33 | content: ""; 34 | position: absolute; 35 | left: 0; 36 | top: 0; 37 | width: 0px; 38 | height: 94%; 39 | border-top-left-radius: 4px; 40 | border-bottom-left-radius: 4px; 41 | } 42 | 43 | &:not(:first-child) { 44 | margin-top: 1.5rem; 45 | } 46 | 47 | &__title { 48 | font-weight: 500; 49 | font-size: 15px; 50 | display: flex; 51 | justify-content: space-between; 52 | align-items: center; 53 | padding-bottom: 8px; 54 | border-bottom: 1px solid #eee; 55 | 56 | &.notification__success { 57 | color: $green; 58 | } 59 | &.notification__info { 60 | color: $yellow; 61 | } 62 | &.notification__danger { 63 | color: darken($tulip, 15%); 64 | } 65 | 66 | &-close { 67 | font-size: 13px; 68 | color: #ccc; 69 | cursor: pointer; 70 | 71 | &:hover { 72 | text-decoration: underline; 73 | } 74 | } 75 | } 76 | 77 | &__body { 78 | padding-top: 8px; 79 | font-weight: 500; 80 | font-size: 14px; 81 | color: lighten($bistre, 30%); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /resources/sass/components/_product_card.scss: -------------------------------------------------------------------------------- 1 | .product__card { 2 | position: relative; 3 | width: 100%; 4 | 5 | &-image-container { 6 | position: relative; 7 | 8 | span.discount-badge { 9 | position: absolute; 10 | top: 1rem; 11 | right: 1rem; 12 | z-index: 10; 13 | font-size: 1.5rem; 14 | font-weight: 500; 15 | text-align: right; 16 | color: $white; 17 | padding: 14px 12px; 18 | background: linear-gradient( 19 | to bottom, 20 | darken($tulip, 10%), 21 | darken($tulip, 30%) 22 | ); 23 | border-radius: 50%; 24 | 25 | p { 26 | font-size: 1.2rem; 27 | } 28 | } 29 | } 30 | 31 | &-details-container { 32 | padding-top: 7px; 33 | 34 | p.description { 35 | font-size: 1.4rem; 36 | font-weight: 500; 37 | text-align: left; 38 | } 39 | 40 | div.price-container { 41 | font-size: 1.6rem; 42 | text-align: left; 43 | padding-top: 6px; 44 | font-weight: 500; 45 | display: flex; 46 | align-items: center; 47 | 48 | span.price-with-discount { 49 | color: $bistre; 50 | 51 | & + .original-price { 52 | font-size: 1.2rem; 53 | color: lighten($bistre, 30%); 54 | padding-left: 5px; 55 | } 56 | } 57 | .original-price { 58 | color: $bistre; 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /resources/sass/layout/_banner.scss: -------------------------------------------------------------------------------- 1 | .banner { 2 | display: flex; 3 | height: 25rem; 4 | width: 100%; 5 | 6 | .right { 7 | width: 25rem; 8 | background: $peach; 9 | display: flex; 10 | justify-content: center; 11 | align-items: center; 12 | 13 | a { 14 | flex: 1; 15 | text-align: center; 16 | color: $tulip; 17 | 18 | i { 19 | font-size: 5rem; 20 | } 21 | } 22 | } 23 | 24 | .left { 25 | flex: 1; 26 | background-image: url("./../../img/basics.jpg"); 27 | background-size: cover; 28 | background-position: center 50%; 29 | background-repeat: no-repeat; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /resources/sass/layout/_blog.scss: -------------------------------------------------------------------------------- 1 | .blog { 2 | display: flex; 3 | 4 | .blog-post { 5 | flex: 1; 6 | height: 500px; 7 | background-size: cover; 8 | background-repeat: no-repeat; 9 | background-position: center center; 10 | transition: all 0.4s ease-in-out; 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | position: relative; 15 | 16 | &:first-child { 17 | background-image: linear-gradient( 18 | to bottom, 19 | rgba(49, 44, 38, 0), 20 | rgba(49, 44, 38, 0.25) 21 | ), 22 | url("./../../img/blog-1.jpg"); 23 | } 24 | 25 | &:last-child { 26 | background-image: linear-gradient( 27 | to bottom, 28 | rgba(49, 44, 38, 0), 29 | rgba(49, 44, 38, 0.25) 30 | ), 31 | url("./../../img/blog-2.jpg"); 32 | } 33 | 34 | .overlay { 35 | opacity: 0; 36 | position: absolute; 37 | top: 0; 38 | left: 0; 39 | width: 100%; 40 | height: 100%; 41 | transition: all 0.3s ease-in-out; 42 | background-image: linear-gradient( 43 | to bottom, 44 | rgba(49, 44, 38, 0.2), 45 | rgba(49, 44, 38, 0.7) 46 | ); 47 | } 48 | 49 | &:hover { 50 | .overlay { 51 | opacity: 1; 52 | } 53 | 54 | .blog-details { 55 | transform: translateY(0); 56 | 57 | .btn { 58 | opacity: 1; 59 | transform: translateY(0); 60 | } 61 | } 62 | } 63 | } 64 | } 65 | 66 | .blog-details { 67 | text-align: center; 68 | transform: translateY(2rem); 69 | transition: all 0.3s ease-in-out; 70 | position: relative; 71 | z-index: 5; 72 | 73 | h2 { 74 | color: $white; 75 | font-size: 3.5rem; 76 | text-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5); 77 | } 78 | 79 | h5 { 80 | padding-top: 1rem; 81 | font-family: $body; 82 | font-size: 1.6rem; 83 | color: $white; 84 | font-weight: normal; 85 | text-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3); 86 | } 87 | 88 | .btn { 89 | opacity: 0; 90 | transform: translateY(2rem); 91 | transform-origin: bottom; 92 | transition: all 0.3s ease-in-out; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /resources/sass/layout/_explore.scss: -------------------------------------------------------------------------------- 1 | .explore_page { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: flex-start; 5 | margin-top: 5rem; 6 | position: relative; 7 | 8 | &__sidebar { 9 | flex: 20%; 10 | position: sticky; 11 | top: 7rem; 12 | z-index: 5; 13 | 14 | & .explore_page__navigation { 15 | ul { 16 | list-style: none; 17 | width: 100%; 18 | } 19 | 20 | ul li { 21 | padding: 1.5rem 2rem; 22 | width: 100%; 23 | font-size: 1.6rem; 24 | text-transform: uppercase; 25 | } 26 | } 27 | } 28 | 29 | &__items { 30 | flex: 70%; 31 | display: grid; 32 | grid-template-columns: 1fr 1fr 1fr; 33 | gap: 3px; 34 | 35 | & > a { 36 | width: 100%; 37 | 38 | & img { 39 | max-width: 100%; 40 | } 41 | 42 | &:nth-child(n + 4) { 43 | margin-top: 1.5rem; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /resources/sass/layout/_footer.scss: -------------------------------------------------------------------------------- 1 | footer { 2 | width: 100%; 3 | background-color: lighten($bistre, 80%); 4 | padding: 7rem 0; 5 | 6 | .footer-menu { 7 | display: flex; 8 | justify-content: space-evenly; 9 | align-items: flex-start; 10 | } 11 | 12 | .footer-category { 13 | flex: 1; 14 | h5 { 15 | color: $bistre; 16 | text-transform: uppercase; 17 | font-family: $body; 18 | font-weight: 500; 19 | font-size: 1.4rem; 20 | padding-bottom: 1rem; 21 | } 22 | 23 | a { 24 | color: darken($grullo, 15%); 25 | display: table; 26 | padding-top: 1.5rem; 27 | } 28 | } 29 | 30 | .brand img { 31 | max-width: 18rem; 32 | } 33 | 34 | .company-info { 35 | .social-links { 36 | list-style-type: none; 37 | display: flex; 38 | justify-content: center; 39 | align-items: center; 40 | 41 | li { 42 | margin: 0 1.5rem; 43 | } 44 | 45 | li a { 46 | color: $grullo; 47 | font-size: 1.6rem; 48 | transition: color 0.3s ease; 49 | 50 | &:hover { 51 | color: $bistre; 52 | } 53 | } 54 | } 55 | 56 | .disclaimer { 57 | width: 75%; 58 | color: darken($grullo, 15%); 59 | text-align: center; 60 | margin: 2rem auto; 61 | font-size: 1.4rem; 62 | } 63 | } 64 | 65 | hr { 66 | border: 1px solid darken($grullo, 15%); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /resources/sass/layout/_forgotpassword.scss: -------------------------------------------------------------------------------- 1 | .forget-password__form, 2 | .reset-password__form { 3 | max-width: 80rem; 4 | margin: 3rem auto; 5 | padding: 2rem 0; 6 | display: flex; 7 | flex-direction: column; 8 | align-items: center; 9 | 10 | h2 { 11 | font-size: 3.5rem; 12 | } 13 | 14 | p { 15 | padding: 3.5rem 5rem; 16 | text-align: center; 17 | font-size: 1.4rem; 18 | } 19 | 20 | form { 21 | padding: 1rem 6rem; 22 | 23 | button.btn--primary { 24 | margin: 0 auto; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /resources/sass/layout/_header.scss: -------------------------------------------------------------------------------- 1 | // main element: 5 big images 2 | .main__links { 3 | display: grid; 4 | grid-template-columns: 1fr 1fr 1fr 1fr 1fr; 5 | grid-template-rows: 1fr 1fr; 6 | grid-template-areas: 7 | "main main main pic1 pic2" 8 | "main main main pic3 pic4"; 9 | width: 100%; 10 | height: 85vh; 11 | 12 | & > * { 13 | display: flex; 14 | flex-direction: column; 15 | justify-content: center; 16 | align-items: center; 17 | height: 100%; 18 | background-repeat: no-repeat; 19 | position: relative; 20 | @include headeroverlay(0.05); 21 | 22 | // hidden button in the 4 small blocks 23 | &:not(.main-pic) { 24 | h2 { 25 | color: $white; 26 | font-style: italic; 27 | text-align: center; 28 | text-transform: uppercase; 29 | text-shadow: 2px 2px 20px #888; 30 | padding: 0 1rem; 31 | transform: translateY(2rem); 32 | transition: all 0.3s ease-in-out; 33 | } 34 | 35 | .btn { 36 | opacity: 0; 37 | transform: translateY(2rem); 38 | transform-origin: bottom; 39 | transition: all 0.3s ease-in-out; 40 | } 41 | 42 | &:hover { 43 | h2 { 44 | transform: translateY(0); 45 | } 46 | .btn { 47 | opacity: 1; 48 | transform: translateY(0); 49 | } 50 | } 51 | } 52 | } 53 | 54 | .main-pic { 55 | background-position: center center; 56 | grid-area: main; 57 | 58 | p { 59 | color: $white; 60 | font-size: 1.6rem; 61 | text-align: center; 62 | } 63 | 64 | .title-container { 65 | position: relative; 66 | padding: 3.5rem 0; 67 | 68 | &::before, 69 | &::after { 70 | content: ""; 71 | height: 2px; 72 | width: 10rem; 73 | background: $white; 74 | position: absolute; 75 | left: 50%; 76 | right: 50%; 77 | transform: translate(-50%, -50%); 78 | } 79 | 80 | &::before { 81 | top: 0; 82 | } 83 | 84 | &::after { 85 | bottom: 0; 86 | } 87 | } 88 | } 89 | 90 | .pic-1 { 91 | grid-area: pic1; 92 | background-position: center; 93 | } 94 | .pic-2 { 95 | grid-area: pic2; 96 | background-position: 75% center; 97 | } 98 | .pic-3 { 99 | grid-area: pic3; 100 | background-position: center 70%; 101 | } 102 | .pic-4 { 103 | grid-area: pic4; 104 | background-position: left center; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /resources/sass/layout/_navbar.scss: -------------------------------------------------------------------------------- 1 | // navbar 2 | #menu { 3 | padding-top: 2rem; 4 | position: relative; 5 | transition: all 0.1s ease; 6 | background-color: $white; 7 | 8 | //responsive buttons 9 | .menu__responsive { 10 | display: none; 11 | justify-content: space-between; 12 | align-items: center; 13 | } 14 | 15 | //navbar links and logo 16 | .menu__links { 17 | display: flex; 18 | justify-content: space-between; 19 | align-items: center; 20 | 21 | .brand { 22 | &::before { 23 | display: none; 24 | } 25 | 26 | a img { 27 | max-height: 6rem; 28 | transition: all 0.3s ease; 29 | } 30 | } 31 | 32 | .links { 33 | display: flex; 34 | justify-content: flex-end; 35 | 36 | a { 37 | font-size: 1.6rem; 38 | text-transform: uppercase; 39 | 40 | &:not(:first-child) { 41 | margin-left: 3rem; 42 | } 43 | } 44 | 45 | form.link-form { 46 | margin-left: 3rem; 47 | } 48 | } 49 | } 50 | 51 | // popup content 52 | .menu__popup { 53 | background: $white; 54 | display: flex; 55 | flex-direction: column; 56 | position: absolute; 57 | top: 110%; 58 | right: 50%; 59 | z-index: -5; 60 | opacity: 0; 61 | visibility: hidden; 62 | transform: translate(50%, 0); 63 | box-shadow: 0px 3px 5px 2px rgba(0, 0, 0, 0.25); 64 | -webkit-box-shadow: 0px 3px 5px 2px rgba(0, 0, 0, 0.25); 65 | -moz-box-shadow: 0px 3px 5px 2px rgba(0, 0, 0, 0.25); 66 | 67 | &.active { 68 | z-index: 999; 69 | opacity: 1; 70 | visibility: visible; 71 | } 72 | 73 | button#close-popup { 74 | font-size: 3.2rem; 75 | position: absolute; 76 | top: 1rem; 77 | right: 2rem; 78 | color: #000; 79 | padding: 0 1rem; 80 | border-radius: 5px; 81 | background: $white; 82 | } 83 | 84 | .popup__content { 85 | padding: 4rem 6rem 9rem 6rem; 86 | max-height: 80vh; 87 | overflow: auto; 88 | 89 | &:not(.active) { 90 | display: none; 91 | } 92 | 93 | h3 { 94 | font-size: 24px; 95 | text-align: center; 96 | } 97 | 98 | // login form structure 99 | .popup_login { 100 | display: flex; 101 | flex-wrap: wrap; 102 | justify-content: space-between; 103 | align-items: flex-start; 104 | 105 | & > * { 106 | width: 50%; 107 | } 108 | 109 | .greeting { 110 | display: flex; 111 | flex-direction: column; 112 | align-items: center; 113 | padding-right: 3rem; 114 | 115 | img { 116 | max-width: 70%; 117 | } 118 | } 119 | } 120 | } 121 | } 122 | 123 | // sticky 124 | &.sticky { 125 | background-color: $white; 126 | padding: 1rem 0; 127 | position: sticky; 128 | top: 0; 129 | z-index: 997; 130 | box-shadow: 0px 3px 5px 2px rgba(0, 0, 0, 0.25); 131 | -webkit-box-shadow: 0px 3px 5px 2px rgba(0, 0, 0, 0.25); 132 | -moz-box-shadow: 0px 3px 5px 2px rgba(0, 0, 0, 0.25); 133 | 134 | .menu__links .brand a img { 135 | max-height: 4rem; 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /resources/sass/layout/_newsletter.scss: -------------------------------------------------------------------------------- 1 | .newsletter { 2 | width: 100%; 3 | padding: 5rem 0; 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | background-image: linear-gradient( 8 | to bottom, 9 | rgba(252, 139, 147, 0.8), 10 | rgba(252, 139, 147, 0.8) 11 | ), 12 | url("./../../img/roses-bg.jpg"); 13 | background-repeat: repeat; 14 | background-position: top left; 15 | 16 | &__container { 17 | width: 60rem; 18 | height: 42rem; 19 | background-color: $white; 20 | display: flex; 21 | flex-direction: column; 22 | justify-content: flex-start; 23 | } 24 | 25 | &__background { 26 | background-image: url("./../../img/newsletter-bg.jpg"); 27 | background-repeat: no-repeat; 28 | background-position: center center; 29 | background-size: cover; 30 | width: 100%; 31 | height: 45%; 32 | } 33 | 34 | &__form { 35 | border-top: 6px double $bistre; 36 | display: flex; 37 | flex-direction: column; 38 | justify-content: center; 39 | align-items: center; 40 | padding: 3rem 6rem; 41 | 42 | h2 { 43 | font-style: italic; 44 | } 45 | 46 | p { 47 | font-size: 1.4rem; 48 | padding-top: 1rem; 49 | } 50 | 51 | .btn { 52 | padding: 1.3rem 3rem; 53 | margin: 0 auto; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /resources/sass/layout/_offers.scss: -------------------------------------------------------------------------------- 1 | .special-offers { 2 | display: flex; 3 | width: 100%; 4 | 5 | &__block { 6 | &--left { 7 | background-color: #f1f1f1; 8 | position: relative; 9 | z-index: 2; 10 | display: flex; 11 | flex-direction: column; 12 | justify-content: center; 13 | align-items: center; 14 | padding: 0 7rem; 15 | margin-right: 3rem; 16 | 17 | h2 { 18 | font-size: 3.8rem; 19 | font-style: italic; 20 | } 21 | 22 | p { 23 | font-size: 1.8rem; 24 | } 25 | 26 | span.discount { 27 | position: absolute; 28 | z-index: -1; 29 | font-size: 30rem; 30 | color: #000; 31 | opacity: 0.15; 32 | font-family: "Arial Narrow", serif; 33 | display: flex; 34 | justify-content: center; 35 | align-items: center; 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /resources/sass/layout/_product_details.scss: -------------------------------------------------------------------------------- 1 | .product-container { 2 | margin-top: 5rem; 3 | display: flex; 4 | flex-wrap: wrap; 5 | place-content: flex-start center; 6 | 7 | &__img img { 8 | width: 100%; 9 | } 10 | 11 | &__details { 12 | flex: 1; 13 | padding: 0 5rem; 14 | max-width: 50%; 15 | 16 | .product-price { 17 | display: flex; 18 | align-items: center; 19 | justify-content: flex-start; 20 | 21 | h2.price-to-show { 22 | font-weight: normal; 23 | 24 | & + h3 { 25 | font-weight: normal; 26 | color: lighten($bistre, 30%); 27 | font-size: 1.5rem; 28 | padding-left: 6px; 29 | } 30 | } 31 | } 32 | 33 | p.description { 34 | color: lighten($bistre, 40%); 35 | font-size: 15px; 36 | } 37 | 38 | form { 39 | .details-title { 40 | font-weight: 500; 41 | } 42 | 43 | button[type="submit"] { 44 | width: 25rem; 45 | } 46 | } 47 | 48 | form .sizes { 49 | position: relative; 50 | width: fit-content; 51 | 52 | label { 53 | cursor: pointer; 54 | display: inline-block; 55 | font-size: 1.8rem; 56 | padding: 0.5rem 0.8rem; 57 | border: 1px solid lighten($bistre, 30%); 58 | transition: all 0.3s ease; 59 | 60 | &:hover { 61 | border: 1px solid lighten($bistre, 23%); 62 | background: lighten($peach, 6%); 63 | } 64 | } 65 | 66 | input[type="radio"] { 67 | position: absolute; 68 | left: -9999px; 69 | 70 | &:checked + label { 71 | background: $tulip; 72 | color: $white; 73 | border: 1px solid $tulip; 74 | } 75 | } 76 | } 77 | 78 | form select { 79 | font-size: 1.6rem; 80 | font-weight: 500; 81 | font-family: $body; 82 | padding: 0.8rem; 83 | width: 25rem; 84 | border-radius: 0; 85 | } 86 | } 87 | } 88 | 89 | @media screen and (max-width: 600px) { 90 | .product-container { 91 | place-content: flex-start center; 92 | 93 | &__img { 94 | order: 1; 95 | } 96 | 97 | &__details { 98 | order: 0; 99 | padding: 2.2rem; 100 | max-width: 100%; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /resources/sass/layout/_profile.scss: -------------------------------------------------------------------------------- 1 | .profile-container { 2 | margin-top: 5rem; 3 | padding: 5rem; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | 8 | .profile-content { 9 | background-color: $white; 10 | padding: 4rem; 11 | width: 70%; 12 | box-shadow: 0px 3px 10px 0px rgba(230, 230, 230, 1); 13 | border-radius: 10px; 14 | 15 | .content-section:not(:first-child) { 16 | margin-top: 8rem; 17 | } 18 | 19 | .content-title { 20 | font-family: $heading; 21 | font-size: 3.5rem; 22 | font-style: italic; 23 | position: relative; 24 | width: fit-content; 25 | 26 | span { 27 | position: relative; 28 | z-index: 20; 29 | } 30 | 31 | &::before { 32 | content: ""; 33 | position: absolute; 34 | bottom: 0; 35 | left: 0; 36 | width: 100%; 37 | border: 7px solid lighten($tulip, 10%); 38 | z-index: 15; 39 | } 40 | } 41 | 42 | div.content-body { 43 | font-size: 2rem; 44 | padding-top: 2rem; 45 | 46 | table.account-info { 47 | width: 100%; 48 | 49 | td { 50 | padding: 0.5rem; 51 | 52 | &.title { 53 | font-weight: 600; 54 | } 55 | 56 | &:not(.title) { 57 | text-align: right; 58 | } 59 | } 60 | } 61 | 62 | table.purchases { 63 | width: 100%; 64 | border: 1px solid #ccc; 65 | border-radius: 5px; 66 | border-collapse: collapse; 67 | border-style: hidden; /* hide standard table (collapsed) border */ 68 | box-shadow: 0 0 0 1px #ccc; /* this draws the table border */ 69 | 70 | td { 71 | padding: 1rem; 72 | 73 | &:last-child { 74 | text-align: center; 75 | } 76 | } 77 | 78 | thead { 79 | tr td { 80 | font-weight: 600; 81 | } 82 | } 83 | tbody { 84 | tr td { 85 | font-size: 1.7rem; 86 | border: 1px solid #ccc; 87 | border-bottom: 1px solid transparent; 88 | word-wrap: break-word; 89 | 90 | i.fa-file-download { 91 | color: $grullo; 92 | cursor: pointer; 93 | } 94 | } 95 | } 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /resources/sass/layout/_profile_delete.scss: -------------------------------------------------------------------------------- 1 | .delete-account-container { 2 | text-align: center; 3 | max-width: 600px; 4 | margin: 0 auto; 5 | margin-top: 4rem; 6 | 7 | p { 8 | font-size: 16px; 9 | padding: 0 1rem; 10 | text-align: left; 11 | margin-top: 4rem; 12 | } 13 | 14 | ul { 15 | text-align: left; 16 | font-size: 16px; 17 | margin-top: 2rem; 18 | 19 | li { 20 | margin-left: 30px; 21 | margin-top: 0.5rem; 22 | } 23 | } 24 | 25 | form { 26 | display: flex; 27 | flex-direction: column; 28 | align-items: center; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /resources/sass/main.scss: -------------------------------------------------------------------------------- 1 | @import "abstracts/variables"; 2 | @import "abstracts/helpers"; 3 | @import "abstracts/keyframes"; 4 | @import "abstracts/mixins"; 5 | @import "base/settings"; 6 | @import "base/typography"; 7 | @import "components/buttons"; 8 | @import "components/carousel"; 9 | @import "components/notifications"; 10 | @import "components/product_card"; 11 | @import "layout/banner"; 12 | @import "layout/blog"; 13 | @import "layout/cart_checkout_page"; 14 | @import "layout/explore"; 15 | @import "layout/footer"; 16 | @import "layout/forgotpassword"; 17 | @import "layout/header"; 18 | @import "layout/navbar"; 19 | @import "layout/newsletter"; 20 | @import "layout/offers"; 21 | @import "layout/profile"; 22 | @import "layout/profile_delete"; 23 | @import "layout/product_details"; 24 | @import "responsive/1200"; 25 | @import "responsive/900"; 26 | @import "responsive/600"; 27 | -------------------------------------------------------------------------------- /resources/sass/responsive/_1200.scss: -------------------------------------------------------------------------------- 1 | // 1200 2 | @media screen and (max-width: 1200px) { 3 | html { 4 | font-size: 59%; 5 | } 6 | .container { 7 | max-width: 1100px; 8 | } 9 | .item-details__picture { 10 | flex: 51%; 11 | } 12 | footer { 13 | padding: 5rem 10rem; 14 | } 15 | } 16 | 17 | // 1100 18 | @media screen and (max-width: 1100px) { 19 | html { 20 | font-size: 55%; 21 | } 22 | .container { 23 | max-width: 1000px; 24 | } 25 | .product__card { 26 | width: 230px; 27 | } 28 | .glider-contain.multiple-items .glider { 29 | width: calc(230px * 4); 30 | } 31 | .glider-contain.single-item .glider { 32 | width: 230px; 33 | } 34 | } 35 | 36 | // 1000 37 | @media screen and (max-width: 1000px) { 38 | .container { 39 | max-width: 900px; 40 | } 41 | .product__card { 42 | width: 200px; 43 | } 44 | .glider-contain.multiple-items .glider { 45 | width: calc(200px * 4); 46 | } 47 | .glider-contain.single-item .glider { 48 | width: 200px; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /resources/sass/responsive/_900.scss: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 900px) { 2 | html { 3 | font-size: 50%; 4 | } 5 | .container { 6 | width: 97%; 7 | } 8 | 9 | header { 10 | height: 80vh !important; 11 | } 12 | 13 | .main__links { 14 | grid-template-columns: 1fr 1fr 1fr 1fr; 15 | grid-template-rows: 1fr 1fr 1fr; 16 | grid-template-areas: 17 | "main main main main" 18 | "main main main main" 19 | "pic1 pic2 pic3 pic4"; 20 | } 21 | 22 | .main-pic { 23 | background-position: center 70% !important; 24 | } 25 | 26 | .product__card { 27 | width: 180px; 28 | } 29 | .glider-contain.multiple-items .glider { 30 | width: calc(180px * 4 + 0.25rem * 8); 31 | } 32 | .glider-contain.single-item .glider { 33 | width: 180px; 34 | } 35 | } 36 | 37 | .explore_page { 38 | &__sidebar { 39 | flex: 20%; 40 | } 41 | &__items { 42 | flex: 80%; 43 | 44 | & > a { 45 | margin: 0px; 46 | } 47 | } 48 | } 49 | 50 | @media screen and (max-width: 760px) { 51 | html { 52 | font-size: 42%; 53 | } 54 | form { 55 | .form-group { 56 | grid-column: span 2; 57 | } 58 | 59 | &#register_form button { 60 | width: 100%; 61 | margin: 0 !important; 62 | font-size: 16px; 63 | } 64 | 65 | &#login_form button { 66 | margin: 0 !important; 67 | font-size: 16px; 68 | } 69 | } 70 | #menu .menu__popup .popup__content .popup_login { 71 | & > * { 72 | width: 100%; 73 | } 74 | 75 | .greeting img { 76 | display: none; 77 | } 78 | } 79 | .glider-contain.multiple-items .glider { 80 | width: calc(170px * 3 + 0.25rem * 6); 81 | } 82 | .glider-contain.single-item .glider { 83 | width: 170px; 84 | } 85 | .product__card { 86 | width: 170px; 87 | } 88 | .product__details .product__name { 89 | font-size: 1.8rem; 90 | } 91 | .product__details .product__price span { 92 | font-size: 2rem; 93 | } 94 | 95 | .blog { 96 | flex-direction: column; 97 | 98 | .blog-post { 99 | height: auto; 100 | padding: 10rem 0; 101 | } 102 | 103 | .blog-details { 104 | transform: translateY(0); 105 | 106 | .btn { 107 | transform: translateY(0); 108 | opacity: 1; 109 | } 110 | } 111 | } 112 | } 113 | 114 | // Rules based on device screen height 115 | @media screen and (min-height: 1200px) { 116 | header { 117 | height: 50vh !important; 118 | } 119 | } 120 | 121 | @media screen and (max-height: 400px) { 122 | header { 123 | height: 100vh !important; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /resources/scripts/FormValidations.js: -------------------------------------------------------------------------------- 1 | export default class FormValidations { 2 | constructor() { 3 | this.regex = { 4 | name: /^[a-zA-ZÀ-ÿ ]{2,60}$/, 5 | email: /^[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+.[a-zA-Z]+$/, 6 | password: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{4,}$/ 7 | }; 8 | } 9 | 10 | setFormData(form) { 11 | this.form = form; 12 | this.form_fields = new Object(); 13 | this.form_inputs = this.form.querySelectorAll('input:not([type="checkbox"])'); 14 | this.form_inputs.forEach((input) => { 15 | this.form_fields[input.name] = false; 16 | }); 17 | } 18 | 19 | hasErrors() { 20 | for (let key in this.form_fields) { 21 | if (!this.form_fields[key]) return true; 22 | } 23 | return false; 24 | } 25 | 26 | validateOnSubmit() { 27 | this.form_inputs.forEach((input) => { 28 | if (input.name === "passwordConfirmation") this.passwordConfirmation(); 29 | else 30 | this.form_fields[input.name] = !!this.regex[input.name].test( 31 | input.value.trim() 32 | ); 33 | }); 34 | } 35 | 36 | validateForCart() { 37 | let checked = Array.from(this.form_inputs).find((input) => input.checked); 38 | let child = this.form_inputs[0]; 39 | 40 | if (!(checked === undefined)) this.form_fields.size = true; 41 | } 42 | 43 | passwordConfirmation() { 44 | let passConfirmation = Array.from(this.form_inputs).find( 45 | (input) => input.name === "passwordConfirmation" 46 | ); 47 | let password = Array.from(this.form_inputs).find( 48 | (input) => input.name === "password" 49 | ); 50 | 51 | if (passConfirmation.value === "" || passConfirmation.value !== password.value) 52 | this.form_fields.passwordConfirmation = false; 53 | else this.form_fields.passwordConfirmation = true; 54 | } 55 | 56 | displayInputStatus() { 57 | this.form_inputs.forEach((input) => { 58 | let form_group = input.closest(".form-group"); 59 | let input_icon = form_group.querySelector(`input[name=${input.name}] + i`); 60 | 61 | if (!this.form_fields[input.name]) { 62 | // Error status 63 | form_group.classList.remove("correct"); 64 | form_group.classList.add("incorrect"); 65 | form_group.querySelector(".input-errors").classList.add("active"); 66 | if (!(input_icon === null)) { 67 | input_icon.classList.add("fa-times-circle"); 68 | input_icon.classList.remove("fa-check-circle"); 69 | } 70 | } else { 71 | // Correct status 72 | form_group.classList.remove("incorrect"); 73 | form_group.querySelector(".input-errors").classList.remove("active"); 74 | form_group.classList.add("correct"); 75 | if (!(input_icon === null)) { 76 | input_icon.classList.remove("fa-times-circle"); 77 | input_icon.classList.add("fa-check-circle"); 78 | } 79 | } 80 | }); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /resources/scripts/index.js: -------------------------------------------------------------------------------- 1 | import "../sass/main.scss"; 2 | import UserInterface from "./UserInterface"; 3 | 4 | const UI = new UserInterface(); 5 | UI.starters(); 6 | -------------------------------------------------------------------------------- /resources/views/cache/0204e23766bf00e917b686935a10700ff5958481.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 20 |
21 |
-------------------------------------------------------------------------------- /resources/views/cache/0648c7784edd42fc40a3624191a789edf0253112.php: -------------------------------------------------------------------------------- 1 |

-------------------------------------------------------------------------------- /resources/views/cache/0969d808bcb2e0a2eede2467d3a4ead6c1f94d98.php: -------------------------------------------------------------------------------- 1 | 5 | 6 | -------------------------------------------------------------------------------- /resources/views/cache/0bfef237117c3bb69bd89ec5a98669b206caaa4f.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | About the fit | An ecommerce app made with PHP and vanilla JS 12 | 13 | 14 | 15 | 16 | yieldContent('navbar'); ?> 17 | make('layouts.main', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 18 | make('includes.footer', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 19 | 20 | 21 | 27 | 28 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /resources/views/cache/0c21111f8b9af44f458f361442fbe3b37e88622a.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Special Offers!

4 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Accusantium cum deleniti, reprehenderit fugit sunt tenetur exercitationem voluptate numquam commodi. Tenetur!

Lorem ipsum dolor sit amet consectetur adipisicing elit. Qui odio blanditiis, libero officia debitis consequatur?

5 | -30% 6 |
7 |
8 | make('components.items_w_discount', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 9 |
10 |
-------------------------------------------------------------------------------- /resources/views/cache/0ca8ac16aee9b5b042676f0a207bf3cbe304da9d.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> 4 | 5 | make('components.product_card', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 6 | 7 | popLoop(); $loop = $__env->getLastLoop(); ?> 8 |
9 | 10 | 11 | 12 |
-------------------------------------------------------------------------------- /resources/views/cache/16966b53754835fbc328c312b296e7243ec77f8a.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Create a new password | About the fit 12 | 13 | 14 | 15 | 16 | 17 | make('components.notification', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 18 | 19 | 20 |
21 | 22 | 23 | make('sections.navbar', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 24 | 25 | 26 |
27 |
28 |

Create a new password

29 |

Enter your new password and confirm. The password needs at least:
4 characters, 1 uppercase letter, 1 lowercase letter, 1 number

30 |
31 | 32 |
33 | 34 |
35 | 36 | 37 |
38 |

39 | The password doesn't have the required characters to be valid. 40 |

41 |
42 | 43 | 44 |
45 | 46 |
47 | 48 | 49 |
50 |

Password and confirmation don't match

51 |
52 | 53 |
54 | 57 |
58 |
59 |
60 |
61 | 62 | 63 | make('sections.footer', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 64 | 65 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /resources/views/cache/16cd276ae237d27157de8d33c399f9b0c7546d40.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/195b0dbab34d9ee0769b13513082b723ca4cdb3c.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/27f3eb99fed362cbd85ac92318f44efb4aaa4691.php: -------------------------------------------------------------------------------- 1 |
2 | <?php echo e($description); ?> 3 |
4 |
5 | 6 |

7 |

$

8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | Score: from 555 votes 29 | 30 | 31 |
32 |
33 | make('components/item_options_select_boxes', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 34 |
35 | 36 | 37 | 38 | 39 |
40 |
-------------------------------------------------------------------------------- /resources/views/cache/2cbc33a03a083ea562a65bee0f3b9544b2a00125.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Animi, iste!

5 |

New pieces & Back in stock

6 |
7 | Go shopping! 8 | 9 |
10 | 11 |

Create stunning outfits

12 | See more 13 | 14 |
15 | 16 |

Shoes

17 | See more 18 | 19 |
20 | 21 |

Swimwear

22 | See more 23 | 24 |
25 | 26 |

Accessories

27 | See more 28 | 29 |
30 |
31 | 32 |
33 |
34 |

Special Offers!

35 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Accusantium cum deleniti, reprehenderit fugit sunt tenetur exercitationem voluptate numquam commodi. Tenetur!

36 |
37 |
RIGHT
38 |
39 | 40 |
41 | sljsl 42 |
43 | 44 |
45 | CATCHING TEXT OR BLOG POST 46 |
47 | -------------------------------------------------------------------------------- /resources/views/cache/2d43cdf30e0985a951df00880517c2a3c363c073.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/31326ef8ca9b14dca9f5af90b766a34152d9b378.php: -------------------------------------------------------------------------------- 1 |
2 | 20 | 21 | 39 |
-------------------------------------------------------------------------------- /resources/views/cache/32803569cf21235b14c0db500429fbe7ca820bed.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/33ac9faf019feb689133bf8f4dd54a490a08cf60.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | About the fit | An ecommerce app made with PHP and vanilla JS 12 | 13 | 14 | 15 | 16 | 17 | make('components.notification', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 18 | 19 | 20 |
21 | 22 | 23 | make('sections.navbar', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 24 | 25 | 26 | make('sections.header', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 27 | 28 | 29 | make('sections.offers', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 30 | 31 | 32 | make('sections.banner', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 33 | 34 | 35 | make('sections.best_sellers', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 36 | 37 | 38 | make('sections.blog', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 39 | 40 | 41 | make('sections.newsletter', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 42 | 43 | 44 | make('sections.footer', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 45 | 46 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /resources/views/cache/3803d8e2f5fae6e82c09f22eff3a7ca495592413.php: -------------------------------------------------------------------------------- 1 | PASSWORD CHANGED
2 | HOME -------------------------------------------------------------------------------- /resources/views/cache/39d73d6082a0423c4f997a6bed2f82b0e13a5763.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/4816d8fe5099c9b75bb6fbd6a698a220be7195f5.php: -------------------------------------------------------------------------------- 1 | 34 | -------------------------------------------------------------------------------- /resources/views/cache/48b46a5eeb7d0a98d777ce32263f438bd4d70113.php: -------------------------------------------------------------------------------- 1 |

Reset password

2 |

Please click here to reset your password.

3 |

If the link doesn't work, copy and paste this URL in your browser:

4 |

-------------------------------------------------------------------------------- /resources/views/cache/4a7f92372e04ed61575a919b9935054bf17df327.php: -------------------------------------------------------------------------------- 1 |
2 | a nu ma 3 |
-------------------------------------------------------------------------------- /resources/views/cache/510999beb41979dcc3381a53a73bb3e522575fc1.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/599337bdb4ee7c5dbee7b2e49db9f3b96837f9ea.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | About the fit | An ecommerce app made with PHP and vanilla JS 11 | 12 | 13 | 14 | 15 | make('includes.navbar', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 16 | make('layout.main', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 17 | make('includes.footer', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 18 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /resources/views/cache/5fb3cefc467fd86b663fe4e1251ea831039e187d.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Delete Account | About the fit 12 | 13 | 14 | 15 | 16 | 17 | make('components.notification', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 18 | 19 | 20 |
21 | 22 | 23 | make('sections.navbar', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 24 | 25 | 26 |
27 |
28 |

Confirm Account Deletion

29 |

We're sorry you want to leave. We need you to confirm that this is actually what you want to do and that you're aware of what will happen. If you click the "Delete my account" button, the following information will be deleted permanently:

30 | 36 |

Do you want to continue?

37 | 38 |
39 | 42 | 43 | 44 | No, go back 45 | 46 |
47 |
48 |
49 | 50 | 51 | make('sections.footer', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 52 | 53 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /resources/views/cache/623f8a09abdd2900be2989b87e16705bbee14d95.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/6c59ac17e7cc293ad2f40255ea65fc53ba4abb88.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | 15 | 16 | 26 |
27 | 28 | 29 |
30 | 41 | 42 | 52 |
-------------------------------------------------------------------------------- /resources/views/cache/70bf6432eb73d4cf97f7be29cc4fe0eca08db832.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | discount > 0): ?> 5 | 6 | discount); ?>%

OFF

7 |
8 | 9 | <?php echo e($item->description); ?> 10 |
11 | 12 |
13 |

description); ?>

14 |
15 | discount > 0): ?> 16 | 17 | $ 18 | 19 | 20 | (lowered from $price); ?>) 21 | 22 | $price); ?> 23 | 24 |
25 |
26 |
27 |
-------------------------------------------------------------------------------- /resources/views/cache/711c51d09ddb823cd38d10177e1417ac427ac567.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | About the fit | An ecommerce app made with PHP and vanilla JS 11 | 12 | 13 | 14 | 15 | make('includes.navbar', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 16 | make('layouts.main', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 17 | make('includes.footer', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 18 | 19 | 20 | 26 | 27 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /resources/views/cache/8472375c3b9707168d48d9dbb20ce227161b80ab.php: -------------------------------------------------------------------------------- 1 |

Reset password

2 |

Please click here to reset your password.

3 |

If the link doesn't work, copy and paste this URL in your browser:

4 |

5 |

6 |

This link will be available for the next 2 hours.

-------------------------------------------------------------------------------- /resources/views/cache/8b06564c496026f8b05cf2de898387b4ecff7b56.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | About the fit | An ecommerce app made with PHP and vanilla JS 11 | 12 | 13 | 14 | 15 | make('includes.navbar', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 16 | make('main', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 17 | make('includes.footer', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 18 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /resources/views/cache/8c8167686d51e9cf5328d9973112d1446dc0e543.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | 10 | 11 |
12 | 13 | Your cart is empty 14 |
15 | -------------------------------------------------------------------------------- /resources/views/cache/94cee634e8542085c215ddc4034a52602f284443.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/971a864b76d18014894e8c2e4dbf3f1156181c59.php: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 |
8 | addLoop($__currentLoopData); foreach($__currentLoopData as $notification): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> 9 |
10 | 11 |
12 |
13 | 15 | 16 | Error 17 | 18 | 19 | 20 | Information 21 | 22 | 23 | 24 | Success 25 | 26 |
27 |
Close
28 |
29 | 30 | 31 |
32 | 33 |
    34 | addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> 35 |
  • 36 | popLoop(); $loop = $__env->getLastLoop(); ?> 37 |
38 | 39 | 40 | 41 | 42 |
43 |
44 | popLoop(); $loop = $__env->getLastLoop(); ?> 45 |
46 | -------------------------------------------------------------------------------- /resources/views/cache/9d044fdd0fce11d9cf481fcba8a8c3c0203af841.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/9ee9e845dd7474f4f559469a3124162ebd3e65db.php: -------------------------------------------------------------------------------- 1 | Reset password 2 | 3 | Please go to the following link to proceed to create a new password for your account: 4 | 5 | 6 | 7 | 8 | This link will be available for the next 2 hours. -------------------------------------------------------------------------------- /resources/views/cache/b13b75b834d09fc8150a4e8a2cf9876f5ba4bb59.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Special Offers!

4 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Accusantium cum deleniti, reprehenderit fugit sunt tenetur exercitationem voluptate numquam commodi. Tenetur!

Lorem ipsum dolor sit amet consectetur adipisicing elit. Qui odio blanditiis, libero officia debitis consequatur?

5 | -30% 6 |
7 |
8 | make('sections.items_w_discount', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 9 |
10 |
-------------------------------------------------------------------------------- /resources/views/cache/b6aaf528f1dd0c642cf3fb7cf9db30a47d947d88.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/b6f14f80e72f4e9231202f0567443f9bbd33757f.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/b99e4da5cc02d998f62060bf64d3d5bc659ccf29.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Women's Clothing | About the fit 12 | 13 | 14 | 15 | 16 | 17 | make('components.notification', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 18 | 19 | 20 |
21 | 22 | 23 | make('sections.navbar', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 24 | 25 | 26 |
27 |
28 | 48 |
49 | 50 |
51 | addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> 52 | make('components.product_card', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 53 | popLoop(); $loop = $__env->getLastLoop(); ?> 54 |
55 |
56 | 57 | 58 | make('sections.footer', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 59 | 60 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /resources/views/cache/b9c807a5a14b50dd9ee4a0d99cd0df1e3f170bdf.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Summer vibes are here!

6 |
Let your summer outfit do the talking
7 | Read Article 8 |
9 |
10 |
11 |
12 |
13 |

How to have a perfect skin

14 |
Create your own skincare routine easily
15 | Read Article 16 |
17 |
18 |
-------------------------------------------------------------------------------- /resources/views/cache/bb968e55a5fc6f228e04184a2dddc86e4771a4a4.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Animi, iste!

5 |

New pieces & Back in stock

6 |
7 | Go shopping! 8 | 9 |
10 | 11 |

Create stunning outfits

12 | See more 13 | 14 |
15 | 16 |

Shoes

17 | See more 18 | 19 |
20 | 21 |

Swimwear

22 | See more 23 | 24 |
25 | 26 |

Accessories

27 | See more 28 | 29 |
30 |
31 | 32 |
33 |
34 |

Special Offers!

35 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Accusantium cum deleniti, reprehenderit fugit sunt tenetur exercitationem voluptate numquam commodi. Tenetur!

36 | -30% 37 |
38 |
39 | make('components.items_w_discount', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 40 |
41 |
42 | 43 |
44 | sljsl 45 |
46 | 47 |
48 | CATCHING TEXT OR BLOG POST 49 |
50 | -------------------------------------------------------------------------------- /resources/views/cache/bef18315e1a7af5021e5346fb5590c3eee540630.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/c0c8ff197a01e9033b120ec99e4f0ff850163e38.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Create a new password | About the fit 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | make('sections.navbar', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 21 | 22 | 23 |
24 |
25 |

Create a new password

26 |

Enter your new password and confirm. The password needs at least: 4 characters, 1 uppercase letter, 1 lowercase letter, 1 number

27 |
28 |
29 | 30 |
31 | 32 | 33 |
34 |

35 | The email address you entered is invalid or it wasn't found in our database. 36 |

37 |
38 | 39 |
40 | 43 |
44 |
45 |
46 |
47 | 48 | 49 | make('sections.footer', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 50 | 51 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /resources/views/cache/c1a1768215f5223c70ded4301734da943800dae9.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Create a new password | About the fit 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | make('sections.navbar', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 21 | 22 | 23 |
24 |
25 |

Create a new password

26 |

Enter your new password and confirm. The password needs at least: 4 characters, 1 uppercase letter, 1 lowercase letter, 1 number

27 |
28 |
29 | 30 |
31 | 32 | 33 |
34 |

35 | The email address you entered is invalid or it wasn't found in our database. 36 |

37 |
38 | 39 |
40 | 43 |
44 |
45 |
46 |
47 | 48 | 49 | make('sections.footer', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 50 | 51 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /resources/views/cache/d537ca4801a544909c034202177a4ce0adef6746.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Recover your password | About the fit 12 | 13 | 14 | 15 | 16 | 17 | make('components.notification', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 18 | 19 | 20 |
21 | 22 | 23 | make('sections.navbar', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 24 | 25 | 26 |
27 |
28 |

Forgot your password?

29 |

Please enter the email address you used to create your account, and we'll send you a link to reset your password.

30 |
31 |
32 | 33 |
34 | 35 | 36 |
37 |

38 | The email address you entered is invalid or it wasn't found in our database. 39 |

40 |
41 | 42 |
43 | 46 |
47 |
48 |
49 |
50 | 51 | 52 | make('sections.footer', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 53 | 54 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /resources/views/cache/e14df9a7db001daf89855806d947d971b0762f3a.php: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
-------------------------------------------------------------------------------- /resources/views/cache/e4f26c89cefc8684a0af1ecddfa6f07b81c98195.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> 4 | discount > 0): ?> 5 | make('components.product_card', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> 6 | 7 | popLoop(); $loop = $__env->getLastLoop(); ?> 8 |
9 | 10 | 13 | 16 |
-------------------------------------------------------------------------------- /resources/views/cache/ebf8c0e064599a125f34f81f7daf0e823b065fd0.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/ec95b9867a30b795d3e69a21c4c40933b72c17f7.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/ed137a898b4abcf698c0e2452e30db55de0a319d.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 |

Follow us on instagram

7 |
8 |
9 |
-------------------------------------------------------------------------------- /resources/views/cache/ed74b4d11cda39896da6ffff8afa7214c4d32af2.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 9 |
10 |
LEFT
11 |
RIGHT
12 |
13 | 14 |
15 | CAROUSEL OF ITEMS 16 |
17 | 18 |
19 | CATCHING TEXT OR BLOG POST 20 |
21 | -------------------------------------------------------------------------------- /resources/views/cache/f72eb3562f434107b18b658536c8ce6208632811.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/cache/f83accc77e0be1ad8f493a38313718546a529425.php: -------------------------------------------------------------------------------- 1 | 34 | -------------------------------------------------------------------------------- /resources/views/cache/feb2ed127f60f3c03e441d329fdaa2f6b3ff31b5.php: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 | 21 |
22 | 23 | 24 |
-------------------------------------------------------------------------------- /resources/views/components/login_form.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/components/notification.blade.php: -------------------------------------------------------------------------------- 1 | @php 2 | // Get messages array 3 | $notifications = \App\Controller\Helper\Flash::getMessages(); 4 | @endphp 5 | 6 | @if (!is_null($notifications)) 7 |
8 | @foreach ($notifications as $notification) 9 |
10 | 11 |
12 |
13 | @switch($notification['type']) 14 | @case('danger') 15 | 16 | Error 17 | @break 18 | @case('info') 19 | 20 | Information 21 | @break 22 | @default 23 | 24 | Success 25 | @endswitch 26 |
27 |
Close
28 |
29 | 30 | 31 |
32 | @if (is_iterable($notification['body'])) 33 |
    34 | @foreach ($notification['body'] as $item) 35 |
  • {{ $item }}
  • 36 | @endforeach 37 |
38 | @else 39 | {{ $notification['body'] }} 40 | @endif 41 |
42 |
43 | @endforeach 44 |
45 | @endif -------------------------------------------------------------------------------- /resources/views/components/product_card.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | @if ($item->discount > 0) 5 | 6 | {{ $item->discount }}%

OFF

7 |
8 | @endif 9 | {{ $item->description}} 10 |
11 | 12 |
13 |

{{ $item->description }}

14 |
15 | @if ($item->discount > 0) 16 | 17 | ${{ App\Controller\Merchandise\Products::calculateDiscount($item) }} 18 | 19 | (lowered from ${{ $item->price }}) 20 | @else 21 | ${{ $item->price }} 22 | @endif 23 |
24 |
25 |
26 |
-------------------------------------------------------------------------------- /resources/views/components/register_form.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/email_templates/resetpass_html.blade.php: -------------------------------------------------------------------------------- 1 |

Reset password

2 |

Please click here to reset your password.

3 |

If the link doesn't work, copy and paste this URL in your browser:

4 |

{{ $url }}

5 |

6 |

This link will be available for the next 2 hours.

-------------------------------------------------------------------------------- /resources/views/email_templates/resetpass_txt.blade.php: -------------------------------------------------------------------------------- 1 | Reset password 2 | 3 | Please go to the following link to proceed to create a new password for your account: 4 | 5 | {{ $url }} 6 | 7 | This link will be available for the next 2 hours. -------------------------------------------------------------------------------- /resources/views/layouts/explore.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Women's Clothing | About the fit 12 | 13 | 14 | 15 | 16 | 17 | @include('components.notification') 18 | 19 | 20 |
21 | 22 | 23 | @include('sections.navbar') 24 | 25 | 26 |
27 |
28 | 48 |
49 | 50 |
51 | @foreach ($items as $item) 52 | @include('components.product_card') 53 | @endforeach 54 |
55 |
56 | 57 | 58 | @include('sections.footer') 59 | 60 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /resources/views/layouts/forget_password.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Recover your password | About the fit 12 | 13 | 14 | 15 | 16 | 17 | @include('components.notification') 18 | 19 | 20 |
21 | 22 | 23 | @include('sections.navbar') 24 | 25 | 26 |
27 |
28 |

Forgot your password?

29 |

Please enter the email address you used to create your account, and we'll send you a link to reset your password.

30 |
31 |
32 | 33 |
34 | 35 | 36 |
37 |

38 | The email address you entered is invalid or it wasn't found in our database. 39 |

40 |
41 | 42 |
43 | 46 |
47 |
48 |
49 |
50 | 51 | 52 | @include('sections.footer') 53 | 54 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /resources/views/layouts/home.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | About the fit | An ecommerce app made with PHP and vanilla JS 12 | 13 | 14 | 15 | 16 | 17 | @include('components.notification') 18 | 19 | 20 |
21 | 22 | 23 | @include('sections.navbar') 24 | 25 | 26 | @include('sections.header') 27 | 28 | 29 | @include('sections.offers') 30 | 31 | 32 | @include('sections.banner') 33 | 34 | 35 | @include('sections.best_sellers') 36 | 37 | 38 | @include('sections.blog') 39 | 40 | 41 | @include('sections.newsletter') 42 | 43 | 44 | @include('sections.footer') 45 | 46 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /resources/views/layouts/invoice.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 42 | 43 |
44 |

Invoice

45 | 46 | 47 | 48 | 51 | 54 | 55 | 56 |
49 | Date: {{ $details->created_at }} 50 | 52 | Customer ID: {{ $details->user }} 53 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | @foreach ($items as $item) 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | @endforeach 77 | 78 | 79 | 80 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 |
Ref. No.QuantityDescriptionUnit PriceAmount
{{ $item->product_id }}{{ $item->quantity }}{{ $item->description }}${{ $item->total / $item->quantity}}${{ $item->total }}
Subtotal: 81 | @php 82 | $amount = 0; 83 | foreach($items as $item) { 84 | $amount += $item->total; 85 | } 86 | @endphp 87 | ${{$amount}} 88 |
Shipping:${{ $details->shipping }}
TOTAL:${{ $details->amount }}
100 |
101 | -------------------------------------------------------------------------------- /resources/views/layouts/profile_delete.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Delete Account | About the fit 12 | 13 | 14 | 15 | 16 | 17 | @include('components.notification') 18 | 19 | 20 |
21 | 22 | 23 | @include('sections.navbar') 24 | 25 | 26 |
27 |
28 |

Confirm Account Deletion

29 |

We're sorry you want to leave. We need you to confirm that this is actually what you want to do and that you're aware of what will happen. If you click the "Delete my account" button, the following information will be deleted permanently:

30 | 36 |

Do you want to continue?

37 | 38 |
39 | 42 | 43 | 44 | No, go back 45 | 46 |
47 |
48 |
49 | 50 | 51 | @include('sections.footer') 52 | 53 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /resources/views/layouts/reset_password.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Create a new password | About the fit 12 | 13 | 14 | 15 | 16 | 17 | @include('components.notification') 18 | 19 | 20 |
21 | 22 | 23 | @include('sections.navbar') 24 | 25 | 26 |
27 |
28 |

Create a new password

29 |

Enter your new password and confirm. The password needs at least:
4 characters, 1 uppercase letter, 1 lowercase letter, 1 number

30 |
31 | 32 |
33 | 34 |
35 | 36 | 37 |
38 |

39 | The password doesn't have the required characters to be valid. 40 |

41 |
42 | 43 | 44 |
45 | 46 |
47 | 48 | 49 |
50 |

Password and confirmation don't match

51 |
52 | 53 |
54 | 57 |
58 |
59 |
60 |
61 | 62 | 63 | @include('sections.footer') 64 | 65 | 69 | {{-- --}} 70 | 71 | 72 | -------------------------------------------------------------------------------- /resources/views/sections/banner.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/sections/best_sellers.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 20 | 21 | 39 |
-------------------------------------------------------------------------------- /resources/views/sections/blog.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Summer vibes are here!

6 |
Let your summer outfit do the talking
7 | Read Article 8 |
9 |
10 |
11 |
12 |
13 |

How to have a perfect skin

14 |
Create your own skincare routine easily
15 | Read Article 16 |
17 |
18 |
-------------------------------------------------------------------------------- /resources/views/sections/footer.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/sections/header.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/sections/items_w_discount.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | @foreach ($products as $item) 4 | @if ($item->discount > 0) 5 | @include('components.product_card') 6 | @endif 7 | @endforeach 8 |
9 | 10 | 13 | 16 |
-------------------------------------------------------------------------------- /resources/views/sections/navbar.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/sections/newsletter.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 20 |
21 |
-------------------------------------------------------------------------------- /resources/views/sections/offers.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Special Offers!

4 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Accusantium cum deleniti, reprehenderit fugit sunt tenetur exercitationem voluptate numquam commodi. Tenetur!

Lorem ipsum dolor sit amet consectetur adipisicing elit. Qui odio blanditiis, libero officia debitis consequatur?

5 | -30% 6 |
7 |
8 | @include('sections.items_w_discount') 9 |
10 |
-------------------------------------------------------------------------------- /src/Config/constants.php: -------------------------------------------------------------------------------- 1 | get('', 'ViewLoaders@homepage'); 3 | $router->get('login_form', 'ViewLoaders@loginFormView'); 4 | $router->get('register_form', 'ViewLoaders@signUpFormView'); 5 | $router->get('forgotten-password', 'ViewLoaders@forgottenPassword'); 6 | $router->get('password/reset/{token}', 'Account\Passwords@validateResetLink'); 7 | $router->get('reset-password-form-{id}--{token}', 'ViewLoaders@resetPasswordForm'); 8 | $router->get('profile', 'ViewLoaders@profile'); 9 | $router->get('confirm-delete-account', 'ViewLoaders@deleteAccountForm'); 10 | $router->get('product_details.{item}', 'ViewLoaders@productDetails'); 11 | $router->get('get-cart', 'Merchandise\CartOperations@getJSON'); 12 | $router->get('cart-checkout', 'ViewLoaders@cartPage'); 13 | $router->get('payment_success_{id}', 'Checkout\Orders@completed'); 14 | $router->get('payment_cancelled_{id}', 'Checkout\Orders@cancelled'); 15 | $router->get('explore-{category}', 'ViewLoaders@explore'); 16 | 17 | $router->post('signup', 'Account\Accounts@create'); 18 | $router->post('login', 'Account\Auth@login'); 19 | $router->post('logout', 'Account\Auth@logout'); 20 | $router->post('request-password-reset', 'Account\Passwords@requestReset'); 21 | $router->post('update-forgotten-password_{id}-{token}', 'Account\Passwords@updateForgotten'); 22 | $router->post('update-profile/basic', 'Account\Accounts@updateBasic'); 23 | $router->post('update-profile/password/{id}', 'Account\Passwords@change'); 24 | $router->post('delete-account', 'Account\Accounts@delete'); 25 | $router->post('add-to-cart.{item}', 'Merchandise\CartOperations@add'); 26 | $router->post('change-quantity', 'Merchandise\CartOperations@changeQuantity'); 27 | $router->post('remove-item', 'Merchandise\CartOperations@remove'); 28 | $router->post('payment-process', 'Checkout\Orders@checkout'); 29 | $router->post('print-invoice-{id}/{user}', 'Checkout\Orders@printInvoice'); -------------------------------------------------------------------------------- /src/Config/utilities.php: -------------------------------------------------------------------------------- 1 | auth_controller = new Auth(); 16 | } 17 | 18 | /** 19 | * Process "Sign Up" form to create a user and start a session 20 | * 21 | * @return void 22 | */ 23 | public function create() { 24 | Validations::processForm($_POST); 25 | $form_errors = Validations::getErrors(); 26 | 27 | if (empty($form_errors)) { 28 | $user = new User( 29 | $_POST['name'], 30 | $_POST['email'], 31 | Passwords::hash($_POST['password']) 32 | ); 33 | if (!$user::isEmailInDatabase($_POST['email'])) { 34 | $new_user_id = $user->create(); 35 | $cart = new Cart(); 36 | $cart->create($new_user_id); 37 | $user->setCurrentUser(); 38 | $session = new Session($user->id); 39 | $session->registerStart(); 40 | Flash::addMessage(NEW_USER); 41 | } else 42 | Flash::addMessage(EMAIL_TAKEN, ERROR); 43 | } 44 | else 45 | Flash::addMessage($form_errors, ERROR); 46 | 47 | redirect('/'); 48 | } 49 | 50 | /** 51 | * Get the currently logged user 52 | * 53 | * @return mixed 54 | */ 55 | public function getLoggedUser() { 56 | if (isset($_SESSION['user'])) 57 | return User::findById($_SESSION['user']); 58 | elseif ($this->auth_controller->loginFromCookie()) 59 | return User::findById($_SESSION['user']); 60 | return null; 61 | } 62 | 63 | /** 64 | * Update account basic information: name and email, coming from profile page 65 | * Check which fields are supposed to be changed and send them to the model 66 | * 67 | * @return void 68 | */ 69 | public function updateBasic() { 70 | $validation_errors = Validations::processForm($_POST); 71 | 72 | if (empty($validation_errors)) { 73 | $current_user = $this->getLoggedUser(); 74 | foreach ($_POST as $key => $value) { 75 | if ($_POST[$key] === $current_user->$key) unset($_POST[$key]); 76 | } 77 | 78 | if (empty($_POST)) 79 | Flash::addMessage(NO_CHANGES, INFO); 80 | else { 81 | User::updateBasicInfo($_POST); 82 | Flash::addMessage(DATA_UPDATED); 83 | } 84 | } else 85 | Flash::addMessage($validation_errors, ERROR); 86 | 87 | redirect('/profile'); 88 | } 89 | 90 | /** 91 | * Delete user's account 92 | * 93 | * @return void 94 | */ 95 | public function delete() { 96 | $user = User::findById($_SESSION['user']); 97 | 98 | Session::endSession(); 99 | if (Cookies::findCookie("remember_me")) 100 | $this->auth_controller->forgetLogin(); 101 | 102 | $user_model = new User($user->name, $user->email, $user->password); 103 | $user_model->delete($user->id); 104 | redirect('/'); 105 | } 106 | } -------------------------------------------------------------------------------- /src/Controllers/Checkout/Payments.php: -------------------------------------------------------------------------------- 1 | setUsername($_ENV['PAYPAL_USERNAME']); 17 | $gateway->setPassword($_ENV['PAYPAL_PASSWORD']); 18 | $gateway->setSignature($_ENV['PAYPAL_SIGNATURE']); 19 | $gateway->setTestMode(true); // Set false for production 20 | return $gateway; 21 | } 22 | 23 | /** 24 | * Execute purchase process 25 | * @param array $parameters 26 | * @return void 27 | */ 28 | public function purchase(array $parameters) { 29 | $response = $this->gateway() 30 | ->purchase($parameters) 31 | ->send(); 32 | 33 | return $response; 34 | } 35 | 36 | /** 37 | * After payment process is completed 38 | * @param array $parameters 39 | * @return void 40 | */ 41 | public function complete(array $parameters) { 42 | $response = $this->gateway() 43 | ->completePurchase($parameters) 44 | ->send(); 45 | 46 | return $response; 47 | } 48 | } -------------------------------------------------------------------------------- /src/Controllers/Helpers/Cookies.php: -------------------------------------------------------------------------------- 1 | $message, 23 | 'type' => $type 24 | ]; 25 | } 26 | 27 | /** 28 | * Get all the messages 29 | * 30 | * @return mixed An array with all the messages or null if none set 31 | */ 32 | public static function getMessages() 33 | { 34 | if (isset($_SESSION['notification'])) { 35 | $messages = $_SESSION['notification']; 36 | unset($_SESSION['notification']); 37 | 38 | return $messages; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Controllers/Helpers/Mail.php: -------------------------------------------------------------------------------- 1 | SMTPDebug = 0; 26 | $mail->isSMTP(); //Set PHPMailer to use SMTP. 27 | $mail->Host = "smtp.gmail.com"; //Set SMTP host name 28 | $mail->SMTPAuth = true; //Because SMTP host requires authentication to send email 29 | //Provide username and password 30 | $mail->Username = $brand_email; 31 | $mail->Password = $brand_email_pass; 32 | $mail->SMTPSecure = "tls"; //If SMTP requires TLS encryption then set it 33 | $mail->Port = 587; //Set TCP port to connect to 34 | $mail->From = $brand_email; 35 | $mail->FromName = 'About The Fit'; 36 | 37 | // Destination info 38 | $mail->addAddress($to); 39 | $mail->isHTML(true); 40 | $mail->Subject = $subject; 41 | $mail->Body = $html; 42 | $mail->AltBody = $text; 43 | 44 | // Attachment 45 | if ($attachment !== null) 46 | $mail->addStringAttachment($attachment, $attachment_title); 47 | 48 | $mail->send(); 49 | } catch (Exception $e) { 50 | die("Mailer Error: " . $mail->ErrorInfo); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/Controllers/Helpers/PDF.php: -------------------------------------------------------------------------------- 1 | mpdf = new Mpdf([ 12 | 'margin_left' => 20, 13 | 'margin_right' => 15, 14 | 'margin_top' => 52, 15 | 'margin_bottom' => 25, 16 | 'margin_header' => 10, 17 | 'margin_footer' => 10 18 | ]); 19 | $this->view = new View; 20 | } 21 | 22 | /** 23 | * Create a .pdf document 24 | * 25 | * @param string $title 26 | * @param string $css_path 27 | * @param string $template 28 | * @param array $params 29 | * @return void 30 | */ 31 | public function create($title, $css_path = null, $template, $params = null) { 32 | $this->mpdf->SetTitle($title); 33 | 34 | $html = $this->view->getTemplate($template, $params); 35 | $css = file_get_contents($css_path); 36 | 37 | $this->mpdf->SetWatermarkText("Paid"); 38 | $this->mpdf->showWatermarkText = true; 39 | $this->mpdf->watermark_font = 'DejaVuSansCondensed'; 40 | $this->mpdf->watermarkTextAlpha = 0.1; 41 | $this->mpdf->WriteHTML($css, HTMLParserMode::HEADER_CSS); 42 | $this->mpdf->WriteHTML($html, HTMLParserMode::HTML_BODY); 43 | } 44 | 45 | /** 46 | * Print a .pdf document created previously 47 | * 48 | * @param string $filename 49 | * @return void 50 | */ 51 | public function print(string $filename) { 52 | $this->mpdf->Output($filename, 'D'); 53 | } 54 | 55 | /** 56 | * Email a .pdf document created previously 57 | * 58 | * @param string $to 59 | * @param string $subject 60 | * @param string $text 61 | * @param string $html 62 | * @param string $filename 63 | * @return void 64 | */ 65 | public function email($to, $subject, $text, $html, $filename) { 66 | $pdf_string = $this->mpdf->Output('', 'S'); 67 | Mail::send($to, $subject, $text, $html, $pdf_string, $filename); 68 | } 69 | } -------------------------------------------------------------------------------- /src/Controllers/Helpers/Token.php: -------------------------------------------------------------------------------- 1 | token = $token_value ? $token_value : bin2hex(random_bytes(16)); 18 | $this->secret_key = $_ENV['SECRET_KEY']; 19 | } 20 | 21 | /** 22 | * Return the original token without the hash 23 | * @return string 24 | */ 25 | public function getValue() { 26 | return $this->token; 27 | } 28 | 29 | /** 30 | * Return hashed token 31 | * @return string 32 | */ 33 | public function getHash() { 34 | return hash_hmac('sha256', $this->token, $this->secret_key); 35 | } 36 | } -------------------------------------------------------------------------------- /src/Controllers/Merchandise/CartOperations.php: -------------------------------------------------------------------------------- 1 | cart = new Cart(); 13 | $this->accounts = new Accounts(); 14 | $this->products = new Products(); 15 | } 16 | 17 | /** 18 | * Get the logged user's cart 19 | * @return array Array of objects (cart items) 20 | */ 21 | public function get(): array { 22 | return $this->cart->get(); 23 | } 24 | 25 | /** 26 | * Add an item to the cart or update its values if it's there already 27 | * @param array $params Item's ID 28 | * @return void 29 | */ 30 | public function add(array $params): void { 31 | $this->validateAdding($params['item'], $_POST); 32 | 33 | $subtotal = $this->calculateSubtotal( 34 | $_POST['quantity'], 35 | $this->products->get($params['item'], false) 36 | ); 37 | 38 | if (empty(Validations::getErrors())) { 39 | !!$this->cart->find($params['item']) 40 | ? $this->cart->update($params['item'], $_POST, $subtotal) 41 | : $this->cart->add($params['item'], $_POST, $subtotal); 42 | 43 | Flash::addMessage(ITEM_ADDED); 44 | } 45 | else 46 | Flash::addMessage(Validations::getErrors(), ERROR); 47 | 48 | redirect('/product_details.' . $params['item']); 49 | } 50 | 51 | /** 52 | * Change the quantity for a product from the cart page 53 | * 54 | * @return void 55 | */ 56 | public function changeQuantity(): void { 57 | $subtotal = $this->calculateSubtotal( 58 | intval($_POST['quantity']), 59 | $this->products->get($_POST['item'], false) 60 | ); 61 | 62 | $this->cart->update(intval($_POST['item']), $_POST, $subtotal); 63 | echo json_encode(["subtotal" => $subtotal, "cart_total" => $this->calculateTotal()]); 64 | } 65 | 66 | /** 67 | * Remove item from cart 68 | */ 69 | public function remove() { 70 | $this->cart->remove(intval($_POST['item'])); 71 | echo json_encode(["cart_total" => $this->calculateTotal()]); 72 | } 73 | 74 | /** 75 | * Perform validations before adding an item to the cart 76 | * @param int $item_id The item ID 77 | * @param array $post $_POST data 78 | * @return void 79 | */ 80 | private function validateAdding(int $item_id, array $post): void { 81 | if (is_null($this->accounts->getLoggedUser())) 82 | Validations::setError(LOGIN_REQUIRED); 83 | else { 84 | if (!!$this->products->get($item_id, false)) 85 | $form_validation_errors = Validations::processForm($post); 86 | else 87 | Validations::setError(ERROR_MESSAGE); 88 | } 89 | } 90 | 91 | /** 92 | * Calculate an item's subtotal applying the discount to the price and 93 | * then multiply that by the $quantity value 94 | * @param int $quantity Desired quantity to add 95 | * @param object $item The item object 96 | * @return float Item's subtotal rounded to 2 decimals 97 | */ 98 | private function calculateSubtotal(int $quantity, object $item): float { 99 | return round($quantity * ($item->price - ($item->price * ($item->discount / 100))), 2); 100 | } 101 | 102 | /** 103 | * Get current user's cart total amount 104 | * 105 | * @return float Cart total rounded to 2 decimals 106 | */ 107 | public function calculateTotal(): float { 108 | $cart = $this->get(); 109 | $amount = 0; 110 | foreach($cart as $item) { 111 | $amount += $item->subtotal; 112 | } 113 | 114 | return round($amount, 2); 115 | } 116 | 117 | /** 118 | * Empty the user's cart 119 | * 120 | * @param integer $user_id 121 | * @return void 122 | */ 123 | public function emptyCart(int $user_id) { 124 | $this->cart->empty($user_id); 125 | } 126 | } -------------------------------------------------------------------------------- /src/Controllers/Merchandise/Products.php: -------------------------------------------------------------------------------- 1 | 0) { 20 | foreach ($result as &$item) { 21 | $item->sizes = explode(', ', $item->sizes); 22 | } 23 | } else 24 | $result->sizes = explode(', ', $result->sizes); 25 | 26 | return $result; 27 | } 28 | 29 | /** 30 | * Get all items that pass the provided condition 31 | * Example: get all items with discount 32 | * 33 | * @param string $condition Example: "`discount` > 0" 34 | * @return mixed 35 | */ 36 | public function getAllWith(string $condition) { 37 | return Product::getAllWith($condition); 38 | } 39 | 40 | /** 41 | * Calculate an item's final price after applying the discount 42 | * 43 | * @param object $item Item object with its price and discount value 44 | * @return float Final price with 2 decimals 45 | */ 46 | public static function calculateDiscount(object $item): float { 47 | return round($item->price - ($item->price * ($item->discount / 100)), 2); 48 | } 49 | 50 | /** 51 | * Get all items categories 52 | * 53 | * @return void 54 | */ 55 | public function getCategories() { 56 | return Product::getCategories(); 57 | } 58 | } -------------------------------------------------------------------------------- /src/Core/Database.php: -------------------------------------------------------------------------------- 1 | setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 24 | } 25 | 26 | return $db; 27 | } 28 | } -------------------------------------------------------------------------------- /src/Core/Router.php: -------------------------------------------------------------------------------- 1 | [], 7 | 'POST' => [] 8 | ]; 9 | private $params = []; 10 | 11 | /** 12 | * Used to load routes.php file 13 | * @param string $file Path to the routes.php file 14 | * @return Router Instance of Router class 15 | */ 16 | public static function loadRoutes(string $file) : Router { 17 | $router = new self; 18 | require_once $file; 19 | return $router; 20 | } 21 | 22 | /** 23 | * Add a GET route 24 | * @param string $uri URI to desired controller 25 | * @param string $controller Respective controller and action for the URI 26 | * @return void 27 | */ 28 | public function get(string $uri, string $controller) : void { 29 | $this->routes['GET'][$this->convertToRegex($uri)] = array_combine(['controller', 'method'], explode('@', $controller)); 30 | } 31 | 32 | /** 33 | * Add a POST route 34 | * @param string $uri URI to desired controller 35 | * @param string $controller Respective controller and action for the URI 36 | * @return void 37 | */ 38 | public function post(string $uri, string $controller) : void { 39 | $this->routes['POST'][$this->convertToRegex($uri)] = array_combine(['controller', 'method'], explode('@', $controller)); 40 | } 41 | 42 | /** 43 | * Convert URI to a regular expression in order to have a flexible router for some routes that have an parameters like {id} or {token} 44 | * @param string $uri URI 45 | * @return string $uri Converted URI 46 | */ 47 | private function convertToRegex(string $uri) : string { 48 | // Escape forward slashes 49 | $uri = \preg_replace('/\//', '\\/', $uri); 50 | 51 | // Convert parameter for item ID, user ID, etc 52 | $uri = preg_replace('/\{(id||item||user)\}/', '(?P<\1>\d+)', $uri); 53 | 54 | // Convert parameter {token} 55 | $uri = preg_replace('/\{(token)\}/', '(?P<\1>[\da-f]+)', $uri); 56 | 57 | // Convert parameter {category} 58 | $uri = preg_replace('/\{(category)\}/', '(?P<\1>[a-z]+)', $uri); 59 | 60 | // Add start and end delimeter 61 | $uri = '/^' . $uri . '$/i'; 62 | 63 | return $uri; 64 | } 65 | 66 | /** 67 | * Match URI and method with respective controller and action 68 | * Call controller 69 | * @param string $uri URI to desired controller 70 | * @param string $requestType HTTP method (GET or POST) 71 | * @return void 72 | */ 73 | public function redirect() : void { 74 | $this->setCurrentRoute(getURI(), getMethod()); 75 | $controller = array_shift($this->params); 76 | 77 | try { 78 | if (class_exists($controller)) { 79 | $controller_object = new $controller(); 80 | $action = array_shift($this->params); 81 | $params = !empty($this->params) ? $this->params : null; 82 | 83 | if (is_null($params)) 84 | $controller_object->$action(); 85 | else 86 | $controller_object->$action($params); 87 | } else 88 | throw new \Exception("Controller or method not found", 404); 89 | } catch (\Exception $message) { 90 | die($message); 91 | } 92 | } 93 | 94 | /** 95 | * Set the current route values (controller and params, if there are any) in $this->params 96 | * 97 | * @param string $uri Current URI 98 | * @param string $requestType HTTP Method 99 | * @return void 100 | */ 101 | private function setCurrentRoute($uri, $requestType) : void { 102 | foreach ($this->routes[$requestType] as $route => $params) { 103 | if (preg_match($route, $uri, $matches)) { 104 | // Set namespace 105 | $this->params['controller'] = '\App\Controller\\' . $params['controller']; 106 | // Set method 107 | $this->params['method'] = $params['method']; 108 | 109 | foreach ($matches as $key => $match) { 110 | if (is_string($key)) 111 | $this->params[$key] = $match; 112 | } 113 | } 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /src/Core/View.php: -------------------------------------------------------------------------------- 1 | views = dirname(dirname(__DIR__)) . '/resources/views/'; 10 | $this->cache = dirname(dirname(__DIR__)) . '/resources/views/cache'; 11 | $this->blade = new Blade($this->views, $this->cache); 12 | } 13 | 14 | /** 15 | * Render a view. If the view is the homepage, get session, product, etc. value first 16 | * 17 | * @param string $view 18 | * @param array $params 19 | * @return void 20 | */ 21 | public function render($view, $params = null) { 22 | echo $this->getTemplate($view, $params); 23 | } 24 | 25 | public function getTemplate($view, $params = null) { 26 | if (is_null($params)) // Load views that require no parameters 27 | return $this->blade->view()->make($view)->render(); 28 | else // Load view with parameters 29 | return $this->blade->view()->make($view)->with($params)->render(); 30 | } 31 | } -------------------------------------------------------------------------------- /src/models/Authentication/Password.php: -------------------------------------------------------------------------------- 1 | prepare("UPDATE `user` SET `password` = :p WHERE `id` = :u"); 21 | $stmt->bindValue(':p', $new_password, \PDO::PARAM_STR); 22 | $stmt->bindValue(':u', $user, \PDO::PARAM_INT); 23 | $stmt->execute(); 24 | } 25 | 26 | /** 27 | * Start password reset process by generating a "reset token" and its expiry time 28 | * 29 | * @param int $user User's ID 30 | * @return void 31 | */ 32 | public function startResetProcess(int $user): void { 33 | $token = new Token; 34 | $hashed_token = $token->getHash(); 35 | $expiry_timestamp = date('Y-m-d H:i:s', time() + 60 * 60 * 2); // 2 hours from now 36 | $db = static::getDB(); 37 | 38 | $stmt = $db->prepare("UPDATE `user` SET password_reset_hash = :token_hash, password_reset_expires_at = :expiry WHERE `id` = :user"); 39 | 40 | $stmt->bindValue(':token_hash', $hashed_token, \PDO::PARAM_STR); 41 | $stmt->bindValue(':expiry', $expiry_timestamp, \PDO::PARAM_STR); 42 | $stmt->bindValue(':user', $user, \PDO::PARAM_INT); 43 | $stmt->execute(); 44 | } 45 | 46 | /** 47 | * Send password reset link to user's email address 48 | * 49 | * @param object $user 50 | * @return void 51 | */ 52 | public function emailResetLink(object $user): void { 53 | $view_class = new View(); 54 | $url = $_ENV['URLROOT'] . '/password/reset/' . $user->password_reset_hash; 55 | $text = $view_class->getTemplate("email_templates/resetpass_txt", ["url" => $url]); 56 | $html = $view_class->getTemplate("email_templates/resetpass_html", ["url" => $url]); 57 | 58 | Mail::send($user->email, 'Password reset', $text, $html); 59 | } 60 | 61 | /** 62 | * Set the password reset columns empty after it was succesfully recovered; 63 | * 64 | * @param integer $user 65 | * @return void 66 | */ 67 | public function clearResetColumns(int $user): void { 68 | $db = static::getDB(); 69 | $stmt = $db->prepare("UPDATE `user` SET `password_reset_hash` = null, `password_reset_expires_at` = null WHERE `id` = :u"); 70 | $stmt->bindValue(':u', $user, \PDO::PARAM_INT); 71 | $stmt->execute(); 72 | } 73 | } -------------------------------------------------------------------------------- /src/models/Merchandise/Product.php: -------------------------------------------------------------------------------- 1 | prepare($sql); 21 | $stmt->bindValue(':id', $id, \PDO::PARAM_INT); 22 | $stmt->execute(); 23 | return $stmt->fetch(\PDO::FETCH_OBJ) ?? null; 24 | } else { 25 | $stmt = $db->prepare($sql); 26 | $stmt->execute(); 27 | return $stmt->fetchAll(\PDO::FETCH_OBJ) ?? null; 28 | } 29 | } 30 | 31 | /** 32 | * Return all items that pass a provided condition 33 | * Example: To get items with discount -> "`discount` > 0" 34 | * @param string $condition Condition to find matches for 35 | * @return mixed Object if product found, null otherwise 36 | */ 37 | public static function getAllWith(string $condition) { 38 | $db = static::getDB(); 39 | $stmt = $db->prepare("SELECT * FROM `product` WHERE {$condition}"); 40 | $stmt->execute(); 41 | return $stmt->fetchAll(\PDO::FETCH_OBJ) ?? null; 42 | } 43 | 44 | /** 45 | * Get all categories 46 | * 47 | * @return void 48 | */ 49 | public static function getCategories() { 50 | $db = static::getDB(); 51 | $stmt = $db->prepare("SELECT * FROM `categories`"); 52 | $stmt->execute(); 53 | return $stmt->fetchAll(\PDO::FETCH_ASSOC) ?? null; 54 | } 55 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 3 | const autoprefixer = require("autoprefixer"); 4 | 5 | module.exports = { 6 | entry: ["babel-polyfill", "./resources/scripts/index.js"], 7 | output: { 8 | filename: "app.js", 9 | path: path.resolve(__dirname, "dist", "assets", "js"), 10 | publicPath: "./dist/assets", 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.js$/, 16 | exclude: [/node_modules/, /vendor/], 17 | use: { 18 | loader: "babel-loader", 19 | options: { 20 | presets: ["@babel/preset-env"], 21 | }, 22 | }, 23 | }, 24 | { 25 | test: /\.scss$/, 26 | use: [ 27 | "style-loader", 28 | MiniCssExtractPlugin.loader, 29 | "css-loader?url=false", 30 | { 31 | loader: "postcss-loader", 32 | options: { 33 | autoprefixer: { 34 | browser: ["last 4 versions"], 35 | }, 36 | plugins: () => [autoprefixer], 37 | }, 38 | }, 39 | "sass-loader", 40 | ], 41 | }, 42 | ], 43 | }, 44 | plugins: [ 45 | new MiniCssExtractPlugin({ 46 | filename: "../styles/[name].css", 47 | }), 48 | ], 49 | }; 50 | --------------------------------------------------------------------------------