├── .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 | 
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 |
--------------------------------------------------------------------------------
/resources/views/cache/0648c7784edd42fc40a3624191a789edf0253112.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/resources/views/cache/0969d808bcb2e0a2eede2467d3a4ead6c1f94d98.php:
--------------------------------------------------------------------------------
1 |
2 |
Welcome back
3 |
Become a Member — you'll enjoy exclusive deals, offers, invites and rewards.
4 |
5 |
6 |
7 |
19 |
--------------------------------------------------------------------------------
/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 |
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 |
3 |
4 |
5 |
6 |
7 |
$
8 |
9 |
27 |
28 |
Score: from 555 votes
29 |
30 |
31 |
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 |
43 |
44 |
45 | CATCHING TEXT OR BLOG POST
46 |
47 |
--------------------------------------------------------------------------------
/resources/views/cache/2d43cdf30e0985a951df00880517c2a3c363c073.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/resources/views/cache/31326ef8ca9b14dca9f5af90b766a34152d9b378.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Best Sellers
4 |
5 |
6 |
7 | addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
8 | make('components.product_card', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>
9 | popLoop(); $loop = $__env->getLastLoop(); ?>
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
New Arrivals
23 |
24 |
25 |
26 | addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
27 | make('components.product_card', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>
28 | popLoop(); $loop = $__env->getLastLoop(); ?>
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/resources/views/cache/32803569cf21235b14c0db500429fbe7ca820bed.php:
--------------------------------------------------------------------------------
1 |
2 | Made by José Abraham Castillo © 2020.
3 |
--------------------------------------------------------------------------------
/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 |
2 |
3 |
4 |
5 |
6 | make('components.login_form', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>
7 |
8 |
9 |
10 | make('components.register_form', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>
11 |
12 |
13 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/resources/views/cache/510999beb41979dcc3381a53a73bb3e522575fc1.php:
--------------------------------------------------------------------------------
1 |
2 | Made by José Abraham Castillo © 2020.
3 |
--------------------------------------------------------------------------------
/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 |
31 | Your user account.
32 | Your session history.
33 | Items you had in your cart.
34 | Reviews you've written
35 |
36 |
Do you want to continue?
37 |
38 |
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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Size
13 |
14 |
15 |
16 |
17 | addLoop($__currentLoopData); foreach($__currentLoopData as $size): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
18 |
19 |
20 |
21 |
22 |
23 |
24 | popLoop(); $loop = $__env->getLastLoop(); ?>
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | 1
34 |
35 |
36 |
37 |
38 | Quantity
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/resources/views/cache/70bf6432eb73d4cf97f7be29cc4fe0eca08db832.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | discount > 0): ?>
5 |
6 | discount); ?>% OFF
7 |
8 |
9 |
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 |
2 | this is the modal bitch
3 |
4 |
5 | make('components.login_form', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>
6 |
7 |
8 |
9 |
10 | REEG
11 |
12 |
--------------------------------------------------------------------------------
/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 |
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 |
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 |
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 |
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 |
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 |
2 |
Become a Member
3 |
Become a Member — you'll enjoy exclusive deals, offers, invites and rewards.
4 |
5 |
6 |
--------------------------------------------------------------------------------
/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 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/resources/views/cache/ebf8c0e064599a125f34f81f7daf0e823b065fd0.php:
--------------------------------------------------------------------------------
1 |
2 | Made by José Abraham Castillo © 2020.
3 |
--------------------------------------------------------------------------------
/resources/views/cache/ec95b9867a30b795d3e69a21c4c40933b72c17f7.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/resources/views/cache/ed137a898b4abcf698c0e2452e30db55de0a319d.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
2 |
Welcome back
3 |
Become a Member — you'll enjoy exclusive deals, offers, invites and rewards.
4 |
5 |
6 |
--------------------------------------------------------------------------------
/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 |
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 |
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 |
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 |
49 | Date: {{ $details->created_at }}
50 |
51 |
52 | Customer ID: {{ $details->user }}
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Ref. No.
61 | Quantity
62 | Description
63 | Unit Price
64 | Amount
65 |
66 |
67 |
68 | @foreach ($items as $item)
69 |
70 | {{ $item->product_id }}
71 | {{ $item->quantity }}
72 | {{ $item->description }}
73 | ${{ $item->total / $item->quantity}}
74 | ${{ $item->total }}
75 |
76 | @endforeach
77 |
78 |
79 | Subtotal:
80 |
81 | @php
82 | $amount = 0;
83 | foreach($items as $item) {
84 | $amount += $item->total;
85 | }
86 | @endphp
87 | ${{$amount}}
88 |
89 |
90 |
91 | Shipping:
92 | ${{ $details->shipping }}
93 |
94 |
95 | TOTAL:
96 | ${{ $details->amount }}
97 |
98 |
99 |
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 |
31 | Your user account.
32 | Your session history.
33 | Items you had in your cart.
34 | Reviews you've written
35 |
36 |
Do you want to continue?
37 |
38 |
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 |
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 |
3 |
Best Sellers
4 |
5 |
6 |
7 | @foreach ($products as $item)
8 | @include('components.product_card')
9 | @endforeach
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
New Arrivals
23 |
24 |
25 |
26 | @foreach ($products2 as $item)
27 | @include('components.product_card')
28 | @endforeach
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
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 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/resources/views/sections/navbar.blade.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/resources/views/sections/newsletter.blade.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------