├── .gitignore
├── Procfile
├── README.md
├── amazon
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
├── cart
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_auto_20201003_1259.py
│ ├── 0003_auto_20201003_1333.py
│ └── __init__.py
├── models.py
├── tests.py
├── urls.py
└── views.py
├── manage.py
├── media
└── products
│ └── images
│ ├── atwood.jpg
│ ├── bengoo.jpg
│ ├── gionee-pioneer-p5l-na-original-imaehhtpmwjg3uuh.jpeg
│ └── job.jpg
├── requirements.txt
└── shop
├── __init__.py
├── admin.py
├── apps.py
├── forms.py
├── migrations
├── 0001_initial.py
├── 0002_auto_20200930_0715.py
├── 0003_auto_20200930_0716.py
├── 0004_auto_20200930_1049.py
├── 0005_product_stock.py
├── 0006_remove_product_available.py
└── __init__.py
├── models.py
├── request_exposer.py
├── static
├── icons
│ ├── favicon.ico
│ ├── favicon.png
│ ├── icon.png
│ └── logo.png
└── shop
│ ├── cart.js
│ ├── message.js
│ ├── script.js
│ └── styles.css
├── templates
├── auth
│ ├── signin.html
│ └── signup.html
├── base.html
├── base_auth.html
└── shop
│ ├── checkout.html
│ ├── detail.html
│ ├── index.html
│ └── shopping_cart.html
├── templatetags
├── __init__.py
└── get_image_url.py
├── tests.py
├── urls.py
└── views.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
98 | __pypackages__/
99 |
100 | # Celery stuff
101 | celerybeat-schedule
102 | celerybeat.pid
103 |
104 | # SageMath parsed files
105 | *.sage.py
106 |
107 | # Environments
108 | .env
109 | .venv
110 | env/
111 | venv/
112 | ENV/
113 | env.bak/
114 | venv.bak/
115 |
116 | # Spyder project settings
117 | .spyderproject
118 | .spyproject
119 |
120 | # Rope project settings
121 | .ropeproject
122 |
123 | # mkdocs documentation
124 | /site
125 |
126 | # mypy
127 | .mypy_cache/
128 | .dmypy.json
129 | dmypy.json
130 |
131 | # Pyre type checker
132 | .pyre/
133 |
134 | # pytype static type analyzer
135 | .pytype/
136 |
137 | # Cython debug symbols
138 | cython_debug/
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: gunicorn amazon.wsgi
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # amazon-clone-django
2 | A simple amazon.com clone built using django.
3 |
4 | For styling I used Sass(scss).
5 |
6 | For serving media files, I used Amazon AWS S3 buckets.
7 |
8 | You can see the live demo here at heroku - [https://amazondjangoclone.herokuapp.com/](https://amazondjangoclone.herokuapp.com/)
9 |
10 |
11 |
--------------------------------------------------------------------------------
/amazon/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-coding-pie/amazon-clone-django/0d1b22aef11493a520f95c745a416b53769b44be/amazon/__init__.py
--------------------------------------------------------------------------------
/amazon/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for amazon project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'amazon.settings')
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/amazon/settings.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 | import os
3 | import django_heroku
4 |
5 | # Build paths inside the project like this: BASE_DIR / 'subdir'.
6 | BASE_DIR = Path(__file__).resolve().parent.parent
7 |
8 | # Quick-start development settings - unsuitable for production
9 | # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
10 |
11 | # SECURITY WARNING: keep the secret key used in production secret!
12 | SECRET_KEY = os.environ.get('SECRET_KEY')
13 | # SECURITY WARNING: don't run with debug turned on in production!
14 | DEBUG = (os.environ.get('DEBUG_VALUE') == 'True')
15 |
16 | ALLOWED_HOSTS = ['amazondjangoclone.herokuapp.com']
17 |
18 |
19 | # Application definition
20 |
21 | INSTALLED_APPS = [
22 | 'django.contrib.admin',
23 | 'django.contrib.auth',
24 | 'django.contrib.contenttypes',
25 | 'django.contrib.sessions',
26 | 'django.contrib.messages',
27 | 'django.contrib.staticfiles',
28 | 'shop.apps.ShopConfig',
29 | 'ckeditor',
30 | 'cart.apps.CartConfig',
31 | ]
32 |
33 | MIDDLEWARE = [
34 | 'django.middleware.security.SecurityMiddleware',
35 | 'django.contrib.sessions.middleware.SessionMiddleware',
36 | 'django.middleware.common.CommonMiddleware',
37 | 'django.middleware.csrf.CsrfViewMiddleware',
38 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
39 | 'django.contrib.messages.middleware.MessageMiddleware',
40 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
41 | 'shop.request_exposer.RequestExposerMiddleware'
42 | ]
43 |
44 | ROOT_URLCONF = 'amazon.urls'
45 |
46 | TEMPLATES = [
47 | {
48 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
49 | 'DIRS': [],
50 | 'APP_DIRS': True,
51 | 'OPTIONS': {
52 | 'context_processors': [
53 | 'django.template.context_processors.debug',
54 | 'django.template.context_processors.request',
55 | 'django.contrib.auth.context_processors.auth',
56 | 'django.contrib.messages.context_processors.messages',
57 | ],
58 | },
59 | },
60 | ]
61 |
62 | WSGI_APPLICATION = 'amazon.wsgi.application'
63 |
64 |
65 | # Database
66 | # https://docs.djangoproject.com/en/3.1/ref/settings/#databases
67 |
68 | DATABASES = {
69 | 'default': {
70 | 'ENGINE': 'django.db.backends.sqlite3',
71 | 'NAME': BASE_DIR / 'db.sqlite3',
72 | }
73 | }
74 |
75 |
76 | # Password validation
77 | # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
78 |
79 | AUTH_PASSWORD_VALIDATORS = [
80 | {
81 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
82 | },
83 | {
84 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
85 | },
86 | {
87 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
88 | },
89 | {
90 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
91 | },
92 | ]
93 |
94 |
95 | # Internationalization
96 | # https://docs.djangoproject.com/en/3.1/topics/i18n/
97 |
98 | LANGUAGE_CODE = 'en-us'
99 |
100 | TIME_ZONE = 'UTC'
101 |
102 | USE_I18N = True
103 |
104 | USE_L10N = True
105 |
106 | USE_TZ = True
107 |
108 |
109 | # Static files (CSS, JavaScript, Images)
110 | # https://docs.djangoproject.com/en/3.1/howto/static-files/
111 |
112 | STATIC_URL = '/static/'
113 | STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
114 |
115 | MEDIA_URL = '/media/'
116 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
117 |
118 | CKEDITOR_CONFIGS = {
119 | 'default': {
120 | 'toolbar': None,
121 | },
122 | }
123 |
124 | CART_SESSION_ID = 'cart'
125 |
126 | LOGIN_URL = "shop:signin"
127 |
128 | django_heroku.settings(locals())
--------------------------------------------------------------------------------
/amazon/urls.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.urls import path, include
3 | from django.conf import settings
4 | from django.conf.urls.static import static
5 |
6 | urlpatterns = [
7 | path('admin/', admin.site.urls),
8 | path('', include('shop.urls')),
9 | path('cart/', include('cart.urls')),
10 | ]
11 |
12 | if settings.DEBUG:
13 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
14 |
--------------------------------------------------------------------------------
/amazon/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for amazon project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'amazon.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/cart/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-coding-pie/amazon-clone-django/0d1b22aef11493a520f95c745a416b53769b44be/cart/__init__.py
--------------------------------------------------------------------------------
/cart/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from .models import CartItem, Cart
3 |
4 | # Register your models here.
5 | @admin.register(CartItem)
6 | class CartItemAdmin(admin.ModelAdmin):
7 | list_display = ['product', 'price', 'quantity', 'cart']
8 |
9 | @admin.register(Cart)
10 | class CartAdmin(admin.ModelAdmin):
11 | list_display = ['user']
--------------------------------------------------------------------------------
/cart/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class CartConfig(AppConfig):
5 | name = 'cart'
6 |
--------------------------------------------------------------------------------
/cart/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.1.1 on 2020-10-03 12:46
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | initial = True
11 |
12 | dependencies = [
13 | ('shop', '0004_auto_20200930_1049'),
14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15 | ]
16 |
17 | operations = [
18 | migrations.CreateModel(
19 | name='Cart',
20 | fields=[
21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
23 | ],
24 | ),
25 | migrations.CreateModel(
26 | name='CartItem',
27 | fields=[
28 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
29 | ('price', models.DecimalField(decimal_places=2, max_digits=10)),
30 | ('quantity', models.IntegerField(default=0)),
31 | ('cart', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cart.cart')),
32 | ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.product')),
33 | ],
34 | ),
35 | ]
36 |
--------------------------------------------------------------------------------
/cart/migrations/0002_auto_20201003_1259.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.1.1 on 2020-10-03 12:59
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ('cart', '0001_initial'),
13 | ]
14 |
15 | operations = [
16 | migrations.AlterField(
17 | model_name='cart',
18 | name='user',
19 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cart', to=settings.AUTH_USER_MODEL),
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/cart/migrations/0003_auto_20201003_1333.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.1.1 on 2020-10-03 13:33
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('cart', '0002_auto_20201003_1259'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='cartitem',
16 | name='cart',
17 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cart_items', to='cart.cart'),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/cart/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-coding-pie/amazon-clone-django/0d1b22aef11493a520f95c745a416b53769b44be/cart/migrations/__init__.py
--------------------------------------------------------------------------------
/cart/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 | from shop.models import Product
4 |
5 | class Cart(models.Model):
6 | user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='cart')
7 |
8 | def __str__(self):
9 | return f"Cart of ${self.user.username}"
10 |
11 | class CartItem(models.Model):
12 | product = models.ForeignKey(Product, on_delete=models.CASCADE)
13 | price = models.DecimalField(max_digits=10, decimal_places=2)
14 | quantity = models.IntegerField(default=0)
15 | cart = models.ForeignKey(Cart, on_delete=models.CASCADE, related_name='cart_items')
16 |
17 | def __str__(self):
18 | return self.product.name
19 |
--------------------------------------------------------------------------------
/cart/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/cart/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from .import views
3 |
4 | app_name = 'cart'
5 |
6 | urlpatterns = [
7 | path('', views.get_cart, name='get_cart'),
8 | path('add_to_cart/', views.add_to_cart, name='add_to_cart'),
9 | path('remove_from_cart/', views.remove_from_cart, name='remove_from_cart'),
10 | path('remove_cart/', views.remove_cart, name='remove_cart'),
11 | ]
--------------------------------------------------------------------------------
/cart/views.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.http import JsonResponse
3 | from django.views.decorators.http import require_http_methods
4 | from shop.models import Product
5 | from .models import CartItem, Cart
6 |
7 | # Create your views here.
8 | def get_cart(request):
9 | # create a session with cart
10 | item_count = 0
11 | total = 0
12 | if request.user.is_authenticated:
13 | if Cart.objects.filter(user=request.user):
14 | cart = Cart.objects.filter(user=request.user)[0]
15 | items = CartItem.objects.filter(cart=cart)
16 |
17 | item_count = sum(item.quantity for item in items)
18 | total = sum(item.quantity * item.price for item in items)
19 | else:
20 | cart = request.session.get(settings.CART_SESSION_ID)
21 |
22 | if not cart:
23 | # create a new cart
24 | cart = request.session[settings.CART_SESSION_ID] = {}
25 |
26 | item_count = sum([product['quantity'] for product in cart.values()])
27 | total = sum([product['quantity'] * float(product['price']) for product in cart.values()])
28 |
29 | return JsonResponse({
30 | 'status': 200,
31 | 'item_count': item_count,
32 | 'total': total,
33 | })
34 |
35 | @require_http_methods(['POST'])
36 | def add_to_cart(request):
37 | id = request.POST.get('id')
38 | item = Product.objects.get(id=id)
39 |
40 | if not item:
41 | return JsonResponse({
42 | 'status': 404,
43 | 'msg': 'Oops item not found'
44 | })
45 |
46 | item_id = str(item.id)
47 |
48 | product_quantity = 1
49 | item_stock = item.stock
50 |
51 | if request.user.is_authenticated:
52 | cart = Cart.objects.get(user=request.user)
53 | if cart:
54 | product = CartItem.objects.filter(product=item, cart=cart)
55 | if item.stock >= 1:
56 | if product:
57 | # if product is already present
58 | product = product[0]
59 | product.quantity += 1
60 | product.save()
61 | item.stock -= 1
62 | item.save()
63 |
64 | product_quantity = product.quantity
65 |
66 | else:
67 | # create a new product
68 | new_item = CartItem(product=item, price=float(item.price), quantity=1, cart=cart)
69 | new_item.save()
70 | item.stock -= 1
71 | item.save()
72 | product_quantity = 1
73 | item_stock = item.stock
74 | else:
75 | cart = request.session.get(settings.CART_SESSION_ID)
76 |
77 | if not cart:
78 | # create a new cart
79 | cart = request.session[settings.CART_SESSION_ID] = {}
80 |
81 | # check if item is already in the cart
82 | if item.stock >= 1:
83 | if item_id in cart.keys():
84 | # update the quantity
85 | cart[item_id]['quantity'] += 1
86 | item.stock -= 1
87 | item.save()
88 |
89 | else:
90 | # add as a new item
91 | cart[item_id] = {
92 | "quantity": 1,
93 | "price": str(item.price)
94 | }
95 | item.stock -= 1
96 | item.save()
97 |
98 | product_quantity = cart[item_id]['quantity']
99 | item_stock = item.stock
100 |
101 | # explicitly say session has been modified
102 | request.session.modified = True
103 |
104 | return JsonResponse({
105 | 'status': 200,
106 | 'msg': 'Item Added',
107 | 'product_quantity': product_quantity,
108 | 'item_stock': item_stock
109 | })
110 |
111 | @require_http_methods(['POST'])
112 | def remove_from_cart(request):
113 | id = request.POST.get('id')
114 | item = Product.objects.get(id=id)
115 |
116 | if not item:
117 | return JsonResponse({
118 | 'status': 404,
119 | 'msg': 'Oops item not found'
120 | })
121 |
122 | item_id = str(item.id)
123 |
124 | product_quantity = 1
125 | item_stock = item.stock
126 |
127 | if request.user.is_authenticated:
128 | cart = Cart.objects.get(user=request.user)
129 | if cart:
130 | product = CartItem.objects.filter(product=item, cart=cart)
131 | if product:
132 | # if product is already present
133 | product = product[0]
134 | if not (product.quantity - 1) < 1:
135 | product.quantity -= 1
136 | item.stock += 1
137 | item.save()
138 | product.save()
139 |
140 | product_quantity = product.quantity
141 | item_stock = item.stock
142 |
143 | return JsonResponse({
144 | 'status': 200,
145 | 'msg': 'Item Added',
146 | 'product_quantity': product_quantity,
147 | 'item_stock': item_stock
148 | })
149 |
150 | @require_http_methods(['POST'])
151 | def remove_cart(request):
152 | id = request.POST.get('id')
153 | item = Product.objects.get(id=id)
154 |
155 | if not item:
156 | return JsonResponse({
157 | 'status': 404,
158 | 'msg': 'Oops item not found'
159 | })
160 |
161 | item_id = str(item.id)
162 |
163 | if request.user.is_authenticated:
164 | cart = Cart.objects.get(user=request.user)
165 | if cart:
166 | product = CartItem.objects.filter(product=item, cart=cart)
167 | if product:
168 | product = product[0]
169 | # if product is already present
170 | # first refill the item.stock
171 | item.stock += product.quantity
172 | item.save()
173 | product.delete()
174 | return JsonResponse({
175 | 'status': 200,
176 | 'msg': 'Item Added'
177 | })
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | """Run administrative tasks."""
9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'amazon.settings')
10 | try:
11 | from django.core.management import execute_from_command_line
12 | except ImportError as exc:
13 | raise ImportError(
14 | "Couldn't import Django. Are you sure it's installed and "
15 | "available on your PYTHONPATH environment variable? Did you "
16 | "forget to activate a virtual environment?"
17 | ) from exc
18 | execute_from_command_line(sys.argv)
19 |
20 |
21 | if __name__ == '__main__':
22 | main()
23 |
--------------------------------------------------------------------------------
/media/products/images/atwood.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-coding-pie/amazon-clone-django/0d1b22aef11493a520f95c745a416b53769b44be/media/products/images/atwood.jpg
--------------------------------------------------------------------------------
/media/products/images/bengoo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-coding-pie/amazon-clone-django/0d1b22aef11493a520f95c745a416b53769b44be/media/products/images/bengoo.jpg
--------------------------------------------------------------------------------
/media/products/images/gionee-pioneer-p5l-na-original-imaehhtpmwjg3uuh.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-coding-pie/amazon-clone-django/0d1b22aef11493a520f95c745a416b53769b44be/media/products/images/gionee-pioneer-p5l-na-original-imaehhtpmwjg3uuh.jpeg
--------------------------------------------------------------------------------
/media/products/images/job.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-coding-pie/amazon-clone-django/0d1b22aef11493a520f95c745a416b53769b44be/media/products/images/job.jpg
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | asgiref==3.2.10
2 | dj-database-url==0.5.0
3 | Django==3.1.1
4 | django-ckeditor==6.0.0
5 | django-heroku==0.3.1
6 | django-js-asset==1.2.2
7 | gunicorn==20.0.4
8 | Pillow==7.2.0
9 | psycopg2==2.8.6
10 | psycopg2-binary==2.8.6
11 | pytz==2020.1
12 | sqlparse==0.3.1
13 | whitenoise==5.2.0
14 |
--------------------------------------------------------------------------------
/shop/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-coding-pie/amazon-clone-django/0d1b22aef11493a520f95c745a416b53769b44be/shop/__init__.py
--------------------------------------------------------------------------------
/shop/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from .models import Category, Product
3 |
4 | # Register your models here.
5 | @admin.register(Category)
6 | class CategoryAdmin(admin.ModelAdmin):
7 | list_display = ('name', )
8 | prepopulated_fields = {
9 | 'slug': ('name', )
10 | }
11 |
12 | @admin.register(Product)
13 | class ProductAdmin(admin.ModelAdmin):
14 | list_display = ('name', 'price', 'manufactured_on', 'category', 'stock')
15 | prepopulated_fields = {
16 | "slug": ("name", )
17 | }
--------------------------------------------------------------------------------
/shop/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ShopConfig(AppConfig):
5 | name = 'shop'
6 |
--------------------------------------------------------------------------------
/shop/forms.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import User
2 | from django import forms
3 |
4 | class SignupForm(forms.Form):
5 | your_name = forms.CharField(label='Your name', max_length=100)
6 | email = forms.EmailField(label='Email')
7 | password = forms.CharField(label='Password', widget=forms.PasswordInput)
8 | re_enter_password = forms.CharField(label='Re-enter password', widget=forms.PasswordInput)
9 |
10 | def clean_your_name(self):
11 | your_name = self.cleaned_data.get('your_name')
12 | user = User.objects.filter(username=your_name)
13 |
14 | if user:
15 | raise forms.ValidationError("Username already exists!")
16 |
17 | return your_name
18 |
19 | def clean_email(self):
20 | email = self.cleaned_data.get('email')
21 | user = User.objects.filter(email=email)
22 |
23 | if user:
24 | raise forms.ValidationError("Email already taken!")
25 |
26 | return email
27 |
28 | def clean_re_enter_password(self):
29 | password = self.cleaned_data.get('password')
30 | re_enter_password = self.cleaned_data.get('re_enter_password')
31 |
32 | if not re_enter_password == password:
33 | raise forms.ValidationError("Password Doesn't match!")
34 |
35 | return re_enter_password
36 |
37 | def save(self, commit=True):
38 | username = self.cleaned_data.get('your_name')
39 | email = self.cleaned_data.get('email')
40 | password = self.cleaned_data.get('password')
41 |
42 | user = User.objects.create_user(
43 | username=username,
44 | email=email,
45 | password=password
46 | )
47 |
48 | return user
49 |
50 | class SignInForm(forms.Form):
51 | email = forms.EmailField(label='Email')
52 | password = forms.CharField(label='Password', widget=forms.PasswordInput)
--------------------------------------------------------------------------------
/shop/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.1.1 on 2020-09-26 14:43
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | initial = True
10 |
11 | dependencies = [
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='Category',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('name', models.CharField(max_length=50)),
20 | ('slug', models.SlugField(max_length=200, unique=True)),
21 | ],
22 | ),
23 | migrations.CreateModel(
24 | name='Product',
25 | fields=[
26 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
27 | ('name', models.CharField(max_length=60)),
28 | ('img', models.ImageField(blank=True, upload_to='products/images')),
29 | ('price', models.DecimalField(decimal_places=2, max_digits=6)),
30 | ('description', models.TextField()),
31 | ('slug', models.SlugField(max_length=200, unique=True)),
32 | ('available', models.BooleanField(default=True)),
33 | ('manufactured_on', models.DateTimeField()),
34 | ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='products', to='shop.category')),
35 | ],
36 | ),
37 | ]
38 |
--------------------------------------------------------------------------------
/shop/migrations/0002_auto_20200930_0715.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.1.1 on 2020-09-30 07:15
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('shop', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterModelOptions(
14 | name='category',
15 | options={'ordering': ('name',), 'verbose_name': 'category', 'verbose_name_plural': 'catergories'},
16 | ),
17 | migrations.AlterModelOptions(
18 | name='product',
19 | options={'ordering': ('name',), 'verbose_name': 'product', 'verbose_name_plural': 'products'},
20 | ),
21 | migrations.AddField(
22 | model_name='product',
23 | name='features',
24 | field=models.TextField(blank=True),
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/shop/migrations/0003_auto_20200930_0716.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.1.1 on 2020-09-30 07:16
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('shop', '0002_auto_20200930_0715'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='product',
15 | name='name',
16 | field=models.CharField(max_length=200),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/shop/migrations/0004_auto_20200930_1049.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.1.1 on 2020-09-30 10:49
2 |
3 | import ckeditor.fields
4 | from django.db import migrations
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('shop', '0003_auto_20200930_0716'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='product',
16 | name='features',
17 | field=ckeditor.fields.RichTextField(blank=True),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/shop/migrations/0005_product_stock.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.1.1 on 2020-10-04 12:00
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('shop', '0004_auto_20200930_1049'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='product',
15 | name='stock',
16 | field=models.IntegerField(default=0),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/shop/migrations/0006_remove_product_available.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.1.1 on 2020-10-05 02:40
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('shop', '0005_product_stock'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='product',
15 | name='available',
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/shop/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-coding-pie/amazon-clone-django/0d1b22aef11493a520f95c745a416b53769b44be/shop/migrations/__init__.py
--------------------------------------------------------------------------------
/shop/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from ckeditor.fields import RichTextField
3 |
4 | # Create your models here.
5 | class Category(models.Model):
6 | name = models.CharField(max_length=50)
7 | slug = models.SlugField(max_length=200, unique=True)
8 |
9 | class Meta:
10 | ordering = ('name', )
11 | verbose_name = 'category'
12 | verbose_name_plural = 'catergories'
13 |
14 | def __str__(self):
15 | return self.name
16 |
17 | class Product(models.Model):
18 | name = models.CharField(max_length=200)
19 | img = models.ImageField(upload_to='products/images', blank=True)
20 | price = models.DecimalField(max_digits=6, decimal_places=2)
21 | stock = models.IntegerField(default=0)
22 | description = models.TextField()
23 | slug = models.SlugField(max_length=200, unique=True)
24 | manufactured_on = models.DateTimeField()
25 | category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products')
26 | features = RichTextField(blank=True)
27 |
28 | class Meta:
29 | ordering = ('name', )
30 | verbose_name = 'product'
31 | verbose_name_plural = 'products'
32 |
33 | def __str__(self):
34 | return self.name
--------------------------------------------------------------------------------
/shop/request_exposer.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from . import models
3 |
4 | def RequestExposerMiddleware(get_response):
5 | def middleware(request):
6 | models.exposed_request = request
7 | response = get_response(request)
8 | return response
9 |
10 | return middleware
--------------------------------------------------------------------------------
/shop/static/icons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-coding-pie/amazon-clone-django/0d1b22aef11493a520f95c745a416b53769b44be/shop/static/icons/favicon.ico
--------------------------------------------------------------------------------
/shop/static/icons/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-coding-pie/amazon-clone-django/0d1b22aef11493a520f95c745a416b53769b44be/shop/static/icons/favicon.png
--------------------------------------------------------------------------------
/shop/static/icons/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-coding-pie/amazon-clone-django/0d1b22aef11493a520f95c745a416b53769b44be/shop/static/icons/icon.png
--------------------------------------------------------------------------------
/shop/static/icons/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-coding-pie/amazon-clone-django/0d1b22aef11493a520f95c745a416b53769b44be/shop/static/icons/logo.png
--------------------------------------------------------------------------------
/shop/static/shop/cart.js:
--------------------------------------------------------------------------------
1 | document.addEventListener('DOMContentLoaded', () => {
2 | const item_count = document.querySelector('.item_count');
3 | const addToCartBtns = document.querySelectorAll('.add_to_cart_btn');
4 |
5 | const upBtns = document.querySelectorAll('.up_btn');
6 | const downBtns = document.querySelectorAll('.down_btn');
7 | const numbers = document.querySelectorAll('.number');
8 | const deleteBtns = document.querySelectorAll('.delete_btn');
9 |
10 | const total = document.querySelector('.total');
11 | const item_total_count = document.querySelector('.item_total_count');
12 |
13 | const availability = document.querySelectorAll('.cart_page .availability');
14 |
15 | const getCookie = (name) => {
16 | let cookieValue = null;
17 | if (document.cookie && document.cookie !== '') {
18 | const cookies = document.cookie.split(';');
19 | for (let i = 0; i < cookies.length; i++) {
20 | const cookie = cookies[i].trim();
21 | if (cookie.substring(0, name.length + 1) === (name + '=')) {
22 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
23 | break;
24 | }
25 | }
26 | }
27 | return cookieValue;
28 | }
29 | const csrftoken = getCookie('csrftoken');
30 |
31 | const getTotal = (isInCart) => {
32 | // fetch a request to /cart
33 | fetch('/cart')
34 | .then(res => res.json())
35 | .then(data => {
36 | if (data.status === 200) {
37 | if (data.item_count > 99) {
38 | data.item_count = "99+"
39 | }
40 | item_count.innerHTML = data.item_count;
41 | if (isInCart) {
42 | total.innerHTML = `$${data.total}`;
43 | if (data.item_count > 1) {
44 | item_total_count.innerHTML = `${data.item_count} items`;
45 | } else {
46 | item_total_count.innerHTML = `${data.item_count} item`;
47 | }
48 | }
49 | }
50 | })
51 | .catch(e => {
52 | console.log(e)
53 | })
54 | }
55 |
56 | const addToCart = (e, isInCart) => {
57 | let id = e.target.dataset.id;
58 |
59 | const formData = new FormData();
60 | formData.append("id", id);
61 |
62 | // fetch POST request to /cart/add_to_cart/
63 | fetch('/cart/add_to_cart/', {
64 | method: 'POST',
65 | body: formData,
66 | headers: {
67 | 'X-CSRFToken': csrftoken
68 | }
69 | })
70 | .then(res => res.json())
71 | .then(data => {
72 | if (data.status === 200) {
73 | getTotal(isInCart);
74 |
75 | if (isInCart) {
76 | numbers.forEach(number => {
77 | if (number.dataset.id === id) {
78 | number.innerHTML = data.product_quantity
79 | }
80 | });
81 |
82 | if (data.product_quantity > 1) {
83 | downBtns.forEach(btn => {
84 | if (btn.dataset.id === e.target.dataset.id) {
85 | btn.classList.remove('disabled');
86 | }
87 | });
88 | }
89 |
90 | if (data.item_stock <= 0) {
91 | availability.forEach(item => {
92 | if (item.dataset.id === e.target.dataset.id) {
93 | item.innerHTML = 'Out Of Stock';
94 | item.classList.remove('available');
95 | item.classList.add('not_available');
96 | }
97 | });
98 | e.target.classList.add('disabled');
99 | }
100 | }
101 |
102 | if (data.item_stock <= 0) {
103 | e.target.disabled = true;
104 | }
105 | }
106 | })
107 | .catch(e => [
108 | console.log(e)
109 | ]);
110 | }
111 |
112 | const removeFromCart = (e) => {
113 | let id = e.target.dataset.id;
114 |
115 | numbers.forEach(number => {
116 | if (number.dataset.id === id) {
117 | if (parseInt(number.innerHTML) > 1) {
118 |
119 | const formData = new FormData();
120 | formData.append("id", id);
121 |
122 | // fetch POST request to /cart/add_to_cart/
123 | fetch('/cart/remove_from_cart/', {
124 | method: 'POST',
125 | body: formData,
126 | headers: {
127 | 'X-CSRFToken': csrftoken
128 | }
129 | })
130 | .then(res => res.json())
131 | .then(data => {
132 | if (data.status === 200) {
133 | getTotal(true);
134 |
135 | numbers.forEach(number => {
136 | if (number.dataset.id === id) {
137 | if (data.product_quantity > 1) {
138 | number.innerHTML = data.product_quantity;
139 | } else if (data.product_quantity === 1) {
140 | number.innerHTML = data.product_quantity;
141 | e.target.classList.add('disabled');
142 | }
143 | }
144 | });
145 |
146 | if (data.item_stock > 0) {
147 | availability.forEach(item => {
148 | if (item.dataset.id === e.target.dataset.id) {
149 | item.innerHTML = 'In Stock';
150 | item.classList.remove('not_available');
151 | item.classList.add('available');
152 | }
153 | })
154 |
155 | upBtns.forEach(btn => {
156 | if (btn.dataset.id === e.target.dataset.id) {
157 | btn.classList.remove('disabled');
158 | }
159 | });
160 | }
161 | }
162 | })
163 | .catch(e => [
164 | console.log(e)
165 | ]);
166 | }
167 | }
168 | });
169 | }
170 |
171 | const removeCart = (e) => {
172 | let id = e.target.dataset.id;
173 |
174 | const formData = new FormData();
175 | formData.append("id", id);
176 |
177 | // fetch POST request to /cart/add_to_cart/
178 | fetch('/cart/remove_cart/', {
179 | method: 'POST',
180 | body: formData,
181 | headers: {
182 | 'X-CSRFToken': csrftoken
183 | }
184 | })
185 | .then(res => res.json())
186 | .then(data => {
187 | if (data.status === 200) {
188 | window.location.reload();
189 | }
190 | })
191 | .catch(e => [
192 | console.log(e)
193 | ]);
194 | }
195 |
196 | addToCartBtns.forEach((btn) => {
197 | btn.addEventListener('click', function (e) {
198 | addToCart(e, false);
199 | })
200 | });
201 |
202 | upBtns.forEach((btn) => {
203 | btn.addEventListener('click', (e) => {
204 | addToCart(e, true);
205 | })
206 | });
207 |
208 | downBtns.forEach((btn) => {
209 | btn.addEventListener('click', (e) => {
210 | removeFromCart(e);
211 | })
212 | });
213 |
214 | deleteBtns.forEach(btn => {
215 | btn.addEventListener('click', (e) => {
216 | removeCart(e);
217 | })
218 | });
219 |
220 | if (window.location.pathname == '/shopping_cart/') {
221 | getTotal(true);
222 | } else {
223 | getTotal();
224 | }
225 | });
226 |
--------------------------------------------------------------------------------
/shop/static/shop/message.js:
--------------------------------------------------------------------------------
1 | const msgContainer = document.querySelector('.msg_container');
2 |
3 | setTimeout(() => {
4 | msgContainer.style.display = "none";
5 | }, 3000)
--------------------------------------------------------------------------------
/shop/static/shop/script.js:
--------------------------------------------------------------------------------
1 | const hamBurgerBtn = document.querySelector('.hamburger_btn');
2 | const sideBox = document.querySelector('.side_box');
3 | const sideBoxContent = document.querySelector('.side_box__content');
4 | const sideBoxBackground = document.querySelector('.side_box__background');
5 |
6 | const navBarSearch = document.querySelector('.navbar__search > form > input');
7 | const navBarMobileSearch = document.querySelector('.navbar__search__mobile > div > form > input')
8 |
9 | hamBurgerBtn.addEventListener('click', (e) => {
10 | // side-box
11 | sideBox.classList.remove('hidden');
12 | }
13 | );
14 |
15 | sideBoxBackground.addEventListener('click', (e) => {
16 | sideBox.classList.add('hidden');
17 | })
18 |
19 | // interrelated search fields
20 | navBarSearch.addEventListener('change', (e) => {
21 | navBarMobileSearch.value = e.target.value;
22 | });
23 |
24 | navBarMobileSearch.addEventListener('change', (e) => {
25 | navBarSearch.value = e.target.value;
26 | });
27 |
--------------------------------------------------------------------------------
/shop/static/shop/styles.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | font-family: Arial, Helvetica, sans-serif; }
5 |
6 | body {
7 | width: 100vw;
8 | height: 100vh;
9 | background: #fff;
10 | overflow-x: hidden; }
11 |
12 | a {
13 | text-decoration: none; }
14 |
15 | nav {
16 | display: flex;
17 | align-items: center;
18 | background: #131921;
19 | padding: 0.7rem 1rem; }
20 |
21 | .container {
22 | padding: 0.7rem 2.5rem 0.7rem 2rem;
23 | margin: 0 auto;
24 | min-height: 80vh;
25 | box-sizing: border-box; }
26 |
27 | .msg_container {
28 | padding: 0rem 2.5rem 0rem 2rem;
29 | margin: 0 auto;
30 | box-sizing: border-box; }
31 |
32 | button:hover {
33 | cursor: pointer; }
34 |
35 | button:disabled {
36 | cursor: not-allowed; }
37 |
38 | .amazon_logo {
39 | width: 6.8rem;
40 | margin: 0.5rem 0rem 1.5rem 0rem; }
41 |
42 | .btn {
43 | color: #111111;
44 | padding: 0.4rem;
45 | outline: none;
46 | border-radius: 3px;
47 | font-size: 0.9rem;
48 | background: #F0BF3C;
49 | border: 1px solid #9b7714;
50 | width: 100%; }
51 |
52 | .messages {
53 | display: flex;
54 | flex-direction: column;
55 | list-style-type: none; }
56 | .messages li {
57 | padding: 0.6rem 0.8rem;
58 | background: #ddd;
59 | border-radius: 3px;
60 | border: 1px solid #cec8c8;
61 | margin-bottom: 0.6rem;
62 | margin-top: 0.6rem; }
63 | .messages .error {
64 | background: rgba(255, 0, 0, 0.5);
65 | border: 1px solid #e72020; }
66 | .messages .success {
67 | background: rgba(0, 255, 0, 0.5);
68 | border: 1px solid #0de40d; }
69 |
70 | .navbar__left {
71 | display: flex;
72 | flex: 2 2;
73 | align-items: center; }
74 |
75 | .navbar__center {
76 | display: flex;
77 | flex: 5 5;
78 | align-items: center; }
79 |
80 | .navbar__right {
81 | display: flex;
82 | flex: 3 3;
83 | align-items: center; }
84 | .navbar__right * {
85 | flex: 1 1; }
86 |
87 | .navbar__icon {
88 | color: #fff;
89 | text-align: center;
90 | cursor: pointer;
91 | margin: 0rem 0.5rem 0rem 0rem; }
92 | .navbar__icon .hamburger_btn {
93 | width: 2rem;
94 | border: 0.06rem solid #eee;
95 | border-radius: 0.1rem;
96 | padding: 0.2rem; }
97 |
98 | .navbar__logo {
99 | flex: 1 1; }
100 | .navbar__logo img {
101 | width: 6rem;
102 | height: 1.8rem; }
103 |
104 | .navbar__search {
105 | width: 100%;
106 | margin-right: 1.2rem; }
107 | .navbar__search form {
108 | display: flex;
109 | background: #fff;
110 | border-top-left-radius: 0.25rem;
111 | border-bottom-left-radius: 0.25rem;
112 | border-top-right-radius: 0.25rem;
113 | border-bottom-right-radius: 0.25rem; }
114 | .navbar__search form input {
115 | flex: 9 9;
116 | border: none;
117 | outline: none;
118 | padding: 0.5rem;
119 | font-size: 1rem;
120 | border-top-left-radius: 0.25rem;
121 | border-bottom-left-radius: 0.25rem; }
122 | .navbar__search form button {
123 | border: none;
124 | outline: none;
125 | cursor: pointer;
126 | border-top-right-radius: 0.25rem;
127 | border-bottom-right-radius: 0.25rem;
128 | padding: 0.2rem;
129 | background: #F3A847; }
130 | .navbar__search form button svg {
131 | flex: 1 1;
132 | width: 1.5rem;
133 | padding: 0.1rem 0.2rem; }
134 |
135 | .navbar__link {
136 | color: #fff;
137 | font-size: 0.9rem;
138 | font-weight: 600;
139 | text-align: right; }
140 |
141 | .navbar__cart {
142 | text-align: center; }
143 | .navbar__cart a .item_count {
144 | font-weight: 600;
145 | color: #F3A847;
146 | font-size: 1.1rem;
147 | left: 1.8rem;
148 | bottom: 1.5rem;
149 | position: relative;
150 | z-index: 99; }
151 | .navbar__cart a svg {
152 | color: #fff;
153 | width: 2.3rem;
154 | z-index: 1100; }
155 |
156 | .navbar__search__mobile {
157 | background: #131921;
158 | padding: 0rem 1rem 0.7rem 1rem;
159 | display: none; }
160 |
161 | /* side-box */
162 | .hidden {
163 | display: none; }
164 |
165 | .side_box {
166 | width: 100vw;
167 | height: 100vh;
168 | box-sizing: border-box;
169 | z-index: 999999; }
170 |
171 | .side_box__content {
172 | width: 22rem;
173 | height: 100%;
174 | position: fixed;
175 | top: 0;
176 | left: 0;
177 | overflow-x: hidden;
178 | background: #fff;
179 | z-index: 999999;
180 | display: flex;
181 | flex-direction: column; }
182 | .side_box__content * {
183 | padding: 1rem 1.3rem; }
184 | .side_box__content .container {
185 | width: 90%;
186 | margin: 0 auto; }
187 | .side_box__content .top {
188 | background-color: #232F3E;
189 | font-size: 1.2rem;
190 | color: #fff;
191 | font-weight: 600; }
192 | .side_box__content .shop_by_category {
193 | text-transform: uppercase;
194 | font-size: 0.9rem;
195 | font-weight: 500;
196 | color: #999; }
197 | .side_box__content .category {
198 | color: #111; }
199 | .side_box__content .category:hover {
200 | background-color: #eee; }
201 |
202 | .side_box__background {
203 | background: #111;
204 | width: 100%;
205 | height: 100%;
206 | z-index: 999;
207 | position: fixed;
208 | top: 0;
209 | left: 0;
210 | opacity: 0.5; }
211 |
212 | .results {
213 | margin-bottom: 0.25rem;
214 | padding: 0.5rem 0px;
215 | border-bottom: 2px solid #DDDDDD; }
216 |
217 | .products {
218 | display: flex;
219 | flex-direction: column;
220 | margin-bottom: 1rem; }
221 |
222 | .product {
223 | display: flex;
224 | padding: 1.8rem 1rem;
225 | box-sizing: border-box;
226 | border-bottom: 2px solid #DDDDDD; }
227 | .product .product__left {
228 | width: 180px;
229 | margin-right: 1.8rem; }
230 | .product .product__left img {
231 | width: 100%;
232 | height: 150px;
233 | object-fit: contain;
234 | object-position: center; }
235 | .product .product__right {
236 | flex: 4 4;
237 | display: flex;
238 | flex-direction: column; }
239 | .product .product__right .category {
240 | font-size: 0.9rem;
241 | margin-bottom: 0.3rem;
242 | color: #0066CC;
243 | font-weight: 600; }
244 | .product .product__right .name {
245 | font-size: 1.3rem;
246 | font-weight: normal;
247 | color: #111;
248 | margin-bottom: 0.6rem; }
249 | .product .product__right .name:hover {
250 | color: #F3A847; }
251 | .product .product__right .price {
252 | font-size: 1.5rem;
253 | margin-bottom: 0.6rem; }
254 | .product .product__right .price::before {
255 | content: "$";
256 | font-size: 0.8rem;
257 | position: relative;
258 | bottom: 0.4rem;
259 | left: 0.3rem; }
260 | .product .product__right .price::after {
261 | content: "00";
262 | font-size: 0.8rem;
263 | position: relative;
264 | bottom: 0.4rem;
265 | right: 0.3rem; }
266 | .product .product__right .availability {
267 | font-size: 0.9rem; }
268 |
269 | .sub_total {
270 | display: flex;
271 | justify-content: flex-end;
272 | font-size: 1.3rem;
273 | margin: 1rem 8% 1rem 1rem; }
274 | .sub_total .total {
275 | margin-left: 0.3rem; }
276 |
277 | .cart_page .product__left {
278 | flex: 1 1; }
279 | .cart_page .product__middle {
280 | flex: 3 3;
281 | display: flex;
282 | flex-direction: column; }
283 | .cart_page .product__middle .name {
284 | font-size: 1.3rem;
285 | font-weight: normal;
286 | color: #0066c0;
287 | margin-bottom: 0.6rem; }
288 | .cart_page .product__middle .name:hover {
289 | color: #F3A847; }
290 | .cart_page .product__middle .availability {
291 | font-size: 0.9rem;
292 | margin-bottom: 0.8rem; }
293 | .cart_page .product__middle svg {
294 | width: 1.4rem;
295 | cursor: pointer; }
296 | .cart_page .product__middle svg:hover {
297 | color: #F0BF3C; }
298 | .cart_page .product__middle svg.disabled {
299 | color: #999;
300 | cursor: not-allowed !important;
301 | pointer-events: none; }
302 | .cart_page .product__middle .bottom {
303 | display: flex;
304 | align-items: center; }
305 | .cart_page .product__middle .bottom .quantity {
306 | display: flex;
307 | align-items: center; }
308 | .cart_page .product__middle .bottom .quantity span {
309 | margin-right: 0.4rem; }
310 | .cart_page .product__middle .bottom .quantity .count {
311 | font-size: 0.9rem;
312 | margin-right: 1rem;
313 | display: flex;
314 | flex-direction: column;
315 | align-items: center; }
316 | .cart_page .product__middle .bottom .line {
317 | color: #DDDDDD; }
318 | .cart_page .product__middle .bottom .delete {
319 | margin-left: 1rem;
320 | font-size: 0.9rem; }
321 | .cart_page .product__middle .bottom .delete .delete_btn {
322 | color: #0066CC;
323 | cursor: pointer; }
324 | .cart_page .product__right {
325 | flex: 1 1; }
326 | .cart_page .product__right .price {
327 | font-size: 1.4rem;
328 | color: #C23832; }
329 |
330 | .available {
331 | color: #28b932; }
332 |
333 | .not_available {
334 | color: #ce2727; }
335 |
336 | h4.category {
337 | margin-bottom: 0.25rem;
338 | padding: 0.5rem 0px;
339 | border-bottom: 2px solid #DDDDDD; }
340 | h4.category a {
341 | font-weight: 400;
342 | color: #0066CC; }
343 |
344 | .product_detail {
345 | margin-bottom: 1rem;
346 | border-bottom: 2px solid #DDDDDD;
347 | padding-bottom: 1.5rem;
348 | display: flex;
349 | margin-top: 1.5rem;
350 | padding-bottom: 3rem; }
351 | .product_detail .product_detail__left {
352 | align-self: center; }
353 | .product_detail .product_detail__left .image {
354 | width: 443px;
355 | max-height: 575px;
356 | object-fit: contain;
357 | object-position: center;
358 | margin-right: 2rem; }
359 | .product_detail .product_detail__right {
360 | flex: 5 5; }
361 | .product_detail .product_detail__right .category {
362 | margin-bottom: 0.6rem; }
363 | .product_detail .product_detail__right .name {
364 | margin-bottom: 0.6rem; }
365 | .product_detail .product_detail__right .price {
366 | color: #777;
367 | margin-bottom: 1.5rem; }
368 | .product_detail .product_detail__right .price span {
369 | font-size: 1.5rem;
370 | color: #C23832; }
371 | .product_detail .product_detail__right .price span::before {
372 | content: "$";
373 | font-size: 0.8rem;
374 | position: relative;
375 | bottom: 0.4rem;
376 | left: 0.02rem; }
377 | .product_detail .product_detail__right .price span::after {
378 | content: "00";
379 | font-size: 0.8rem;
380 | position: relative;
381 | bottom: 0.4rem;
382 | right: 0.02rem; }
383 | .product_detail .buttons {
384 | display: flex;
385 | margin-bottom: 1.5rem; }
386 |
387 | .details {
388 | margin-bottom: 1rem; }
389 | .details h4 {
390 | margin-bottom: 0.25rem;
391 | padding: 0.5rem 0px; }
392 | .details li span {
393 | font-size: 1rem;
394 | font-weight: 600; }
395 |
396 | .about h4 {
397 | margin-bottom: 0.25rem;
398 | padding: 0.5rem 0px; }
399 | .about li {
400 | margin-left: 1rem; }
401 |
402 | .add_to_cart_btn, a.go_to_cart_btn {
403 | padding: 0.4rem;
404 | outline: none;
405 | border-radius: 3px;
406 | font-size: 0.9rem; }
407 |
408 | .add_to_cart_btn {
409 | background: #F0BF3C;
410 | border: 1px solid #9b7714; }
411 |
412 | a.go_to_cart_btn {
413 | background: #E8962E;
414 | border: 1px solid #a8650d;
415 | margin-left: 1.2rem;
416 | color: #111; }
417 |
418 | .checkout_btn_div {
419 | display: flex;
420 | justify-content: center;
421 | align-items: center;
422 | margin: 2rem 0rem; }
423 |
424 | .checkout_btn {
425 | width: 20rem;
426 | text-align: center; }
427 |
428 | .similar_items {
429 | margin-bottom: 1rem;
430 | border-bottom: 2px solid #DDDDDD;
431 | padding-bottom: 1.5rem; }
432 | .similar_items h4 {
433 | font-size: 1.4rem;
434 | margin-bottom: 1.5rem; }
435 | .similar_items .items {
436 | display: flex;
437 | flex-wrap: wrap;
438 | justify-content: space-between; }
439 | .similar_items .items .item {
440 | width: 200px;
441 | display: flex;
442 | flex-direction: column;
443 | align-items: flex-start;
444 | margin-bottom: 1.5rem; }
445 | .similar_items .items .item a {
446 | color: #0066CC; }
447 | .similar_items .items .item img {
448 | max-width: 12rem;
449 | max-height: 12rem;
450 | margin-bottom: 0.6rem; }
451 | .similar_items .items .item .price {
452 | margin: 0.4rem 0rem;
453 | font-size: 1.4rem;
454 | color: #C23832; }
455 | .similar_items .items .item .price::before {
456 | content: "$";
457 | font-size: 0.8rem;
458 | position: relative;
459 | bottom: 0.4rem;
460 | left: 0.02rem; }
461 | .similar_items .items .item .price::after {
462 | content: "00";
463 | font-size: 0.8rem;
464 | position: relative;
465 | bottom: 0.4rem;
466 | right: 0.02rem; }
467 |
468 | .description {
469 | margin-bottom: 1rem; }
470 | .description h4 {
471 | margin-bottom: 0.25rem;
472 | padding: 0rem 0rem 0.5rem 0rem; }
473 |
474 | footer {
475 | display: flex;
476 | flex-direction: column;
477 | text-align: center; }
478 | footer .top {
479 | background: #485769;
480 | padding: 1rem 0rem;
481 | color: #FFFCFA;
482 | font-size: 0.9rem; }
483 | footer .center {
484 | display: flex;
485 | justify-content: center;
486 | background: #232F3E;
487 | padding: 3rem 0rem; }
488 | footer .center div {
489 | display: flex; }
490 | footer .center a {
491 | color: #FFFCFA;
492 | font-size: 0.9rem;
493 | margin-right: 1rem; }
494 | footer .center a:hover {
495 | text-decoration: underline; }
496 | footer .bottom {
497 | background: #131A22;
498 | font-size: 0.8rem;
499 | color: #8D9191;
500 | padding: 0.5rem; }
501 |
502 | .signup, .signin {
503 | display: flex;
504 | flex-direction: column;
505 | align-items: center; }
506 | .signup form, .signin form {
507 | display: flex;
508 | flex-direction: column;
509 | border: 1px solid #DDDDDD;
510 | border-radius: 3px;
511 | padding: 2rem 1.5rem;
512 | width: 20rem;
513 | box-sizing: border-box; }
514 | .signup form ul.errors, .signin form ul.errors {
515 | display: flex;
516 | flex-direction: column;
517 | list-style-type: none; }
518 | .signup form ul.errors li.error, .signin form ul.errors li.error {
519 | font-size: 0.9rem;
520 | color: #ff0000;
521 | margin: 0.2rem 0rem 0.8rem 0rem; }
522 | .signup form h1, .signin form h1 {
523 | font-size: 2rem;
524 | font-weight: 500;
525 | margin-bottom: 0.9rem; }
526 | .signup form p, .signin form p {
527 | width: 100%; }
528 | .signup form label, .signin form label {
529 | font-size: 0.9rem;
530 | font-weight: 700;
531 | color: #111;
532 | padding: 0rem 0rem 0.1rem 0.1rem;
533 | margin-bottom: 0.1rem; }
534 | .signup form input, .signin form input {
535 | width: 100%;
536 | margin-top: 0.2rem;
537 | margin-bottom: 0.8rem;
538 | border: 1px solid #8D9191;
539 | font-size: 1rem;
540 | padding: 0.3rem 0.4rem;
541 | border-radius: 3px;
542 | box-sizing: border-box; }
543 | .signup form input:focus, .signin form input:focus {
544 | outline-color: #F3A847; }
545 | .signup form p.agreement, .signin form p.agreement {
546 | font-size: 0.8rem;
547 | margin: 1.2rem 0rem;
548 | padding-bottom: 1.2rem;
549 | border-bottom: 1px solid #DDDDDD; }
550 | .signup form p.extras, .signin form p.extras {
551 | font-size: 0.9rem; }
552 |
553 | @media only screen and (max-width: 52rem) {
554 | .navbar__left {
555 | flex: 1 1; }
556 |
557 | .navbar__center {
558 | display: none; }
559 |
560 | .navbar__right {
561 | flex: 1 1; }
562 |
563 | .navbar__search__mobile {
564 | display: block; }
565 |
566 | .navbar__search__mobile * {
567 | display: block; }
568 |
569 | .product {
570 | padding: 1.8rem 0rem; }
571 |
572 | .product_detail {
573 | width: 100%;
574 | display: flex;
575 | flex-direction: column;
576 | align-items: center; }
577 | .product_detail .product_detail__left .image {
578 | max-width: 100%;
579 | max-height: 575px;
580 | margin: 0rem 0rem 0.6rem 0rem; }
581 | .product_detail .product_detail__right {
582 | flex: 1 1; }
583 | .product_detail .product_detail__right .category {
584 | margin-bottom: 0.6rem; }
585 | .product_detail .product_detail__right .name {
586 | margin-bottom: 0.6rem; }
587 |
588 | .buttons {
589 | justify-content: center; } }
590 |
591 | /*# sourceMappingURL=styles.css.map */
592 |
--------------------------------------------------------------------------------
/shop/templates/auth/signin.html:
--------------------------------------------------------------------------------
1 | {% extends 'base_auth.html' %}
2 | {% load static %}
3 |
4 | {% block title %}
5 | SignIn
6 | {% endblock %}
7 |
8 | {% block meta %}
9 |
10 | {% endblock %}
11 |
12 | {% block content %}
13 |
21 | Hey thank you for purchasing on Amazon.com. Your items will be delivered to your address soon! 22 |
23 |26 | Continue Shopping 27 |
28 |Price: {{ product.price }}
27 | 41 | 42 |71 | {{ item.price }} 72 |
73 | {% if item.stock > 0 %} 74 | 75 | {% else %} 76 | 77 | {% endif %} 78 |34 | {{ product.price }} 35 |
36 |37 | {% if product.stock > 0 %} 38 | In Stock 39 | {% else %} 40 | Out Of Stock 41 | {% endif %} 42 |
43 |No Products Found!
49 | {% endif %} 50 |31 | {% if item.product.stock > 0 %} 32 | In Stock 33 | {% else %} 34 | Out Of Stock 35 | {% endif %} 36 |
37 |66 | {{ item.price }} 67 |
68 |Your Cart is Empty!
80 | {% endif %} 81 |