diff --git a/src/accounts/forms.py b/src/accounts/forms.py index aa7e2c5..aa0d7b8 100644 --- a/src/accounts/forms.py +++ b/src/accounts/forms.py @@ -5,7 +5,15 @@ from .models import Address, User class AddressForm(forms.ModelForm): class Meta: model = Address - fields = '__all__' + fields = ( + 'first_name', + 'last_name', + 'street_address_1', + 'street_address_2', + 'city', + 'state', + 'postal_code', + ) class AccountCreateForm(UserCreationForm): @@ -16,8 +24,24 @@ class AccountCreateForm(UserCreationForm): class AccountUpdateForm(UserChangeForm): class Meta: model = User - fields = [ + fields = ( 'first_name', 'last_name', 'email', - ] + 'default_shipping_address', + 'addresses', + ) + +class CustomerUpdateForm(forms.ModelForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['default_shipping_address'].queryset = kwargs['instance'].addresses + + class Meta: + model = User + fields = ( + 'first_name', + 'last_name', + 'email', + 'default_shipping_address', + ) diff --git a/src/core/models.py b/src/core/models.py index 9e9082c..a64e201 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -73,6 +73,12 @@ class ProductPhoto(models.Model): def __str__(self): return self.product.name + def delete(self, *args, **kwargs): + storage, path = self.image.storage, self.image.path + + super(ProductPhoto, self).delete(*args, **kwargs) + storage.delete(path) + # def save(self, *args, **kwargs): # super().save(*args, **kwargs) @@ -109,6 +115,9 @@ class Coupon(models.Model): class Meta: ordering = ("code",) + def __str__(self): + return self.name + @property def is_valid(self): today = timezone.localtime(timezone.now()) @@ -224,6 +233,9 @@ class Order(models.Model): def get_absolute_url(self): return reverse('dashboard:order-detail', kwargs={'pk': self.pk}) + class Meta: + ordering = ('-created_at',) + class Transaction(models.Model): diff --git a/src/dashboard/templates/dashboard/coupon_confirm_delete.html b/src/dashboard/templates/dashboard/coupon_confirm_delete.html index a5e20c6..ed1a632 100644 --- a/src/dashboard/templates/dashboard/coupon_confirm_delete.html +++ b/src/dashboard/templates/dashboard/coupon_confirm_delete.html @@ -7,7 +7,7 @@

Coupon

-
{% csrf_token %} + {% csrf_token %}

Are you sure you want to delete "{{ object }}"?

{{ form.as_p }}

diff --git a/src/dashboard/templates/dashboard/coupon_detail.html b/src/dashboard/templates/dashboard/coupon_detail.html index 857efec..aaa3354 100644 --- a/src/dashboard/templates/dashboard/coupon_detail.html +++ b/src/dashboard/templates/dashboard/coupon_detail.html @@ -6,8 +6,8 @@

{{ coupon.name }}

diff --git a/src/dashboard/templates/dashboard/customer_form.html b/src/dashboard/templates/dashboard/customer_form.html index 0c73630..4d157a5 100644 --- a/src/dashboard/templates/dashboard/customer_form.html +++ b/src/dashboard/templates/dashboard/customer_form.html @@ -2,6 +2,7 @@ {% block content %}
+

← Back

Update Customer

diff --git a/src/dashboard/templates/dashboard/order_detail.html b/src/dashboard/templates/dashboard/order_detail.html index e720a18..be1a2de 100644 --- a/src/dashboard/templates/dashboard/order_detail.html +++ b/src/dashboard/templates/dashboard/order_detail.html @@ -29,7 +29,7 @@ {% with product=item.product %}
{{product.productphoto_set.first.image}} -
{{product.name}}
+
{{product.name}}
Grind: {{item.customer_note}}
{{product.sku}} {{item.quantity}} diff --git a/src/dashboard/templates/dashboard/product_create_form.html b/src/dashboard/templates/dashboard/product_create_form.html index 93af236..4716353 100644 --- a/src/dashboard/templates/dashboard/product_create_form.html +++ b/src/dashboard/templates/dashboard/product_create_form.html @@ -2,9 +2,11 @@ {% block content %}
-

Create product

-
- +
+

Create product

+
+
+ {% csrf_token %} {{form.as_p}}

diff --git a/src/dashboard/views.py b/src/dashboard/views.py index 29d798a..7a5a50b 100644 --- a/src/dashboard/views.py +++ b/src/dashboard/views.py @@ -116,9 +116,10 @@ class OrderListView(LoginRequiredMixin, ListView): def get_queryset(self): query = self.request.GET.get('status') - if query: + if query == 'unfulfilled': object_list = Order.objects.filter( - status=query + Q(status=OrderStatus.UNFULFILLED) | + Q(status=OrderStatus.PARTIALLY_FULFILLED) ).order_by( '-created_at' ).select_related( @@ -279,7 +280,7 @@ class CustomerUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = User template_name = 'dashboard/customer_form.html' context_object_name = 'customer' - success_message = '%(name)s saved.' + success_message = 'Customer saved.' fields = ( 'first_name', 'last_name', diff --git a/src/media/products/images/coffee.jpg b/src/media/products/images/coffee.jpg deleted file mode 100644 index 545e3d7..0000000 Binary files a/src/media/products/images/coffee.jpg and /dev/null differ diff --git a/src/media/products/images/coffee_02.jpg b/src/media/products/images/coffee_02.jpg deleted file mode 100644 index 1f55562..0000000 Binary files a/src/media/products/images/coffee_02.jpg and /dev/null differ diff --git a/src/media/products/images/coffee_05.jpg b/src/media/products/images/coffee_05.jpg deleted file mode 100644 index 2285280..0000000 Binary files a/src/media/products/images/coffee_05.jpg and /dev/null differ diff --git a/src/ptcoffee/settings.py b/src/ptcoffee/settings.py index aae9912..dd462df 100644 --- a/src/ptcoffee/settings.py +++ b/src/ptcoffee/settings.py @@ -9,6 +9,8 @@ BASE_DIR = Path(__file__).resolve().parent.parent # Add Your Required Allow Host if DEBUG == False: ALLOWED_HOSTS = ['ptcoffee-dev.windmillapps.org'] +else: + ALLOWED_HOSTS = ['192.168.68.106', '127.0.0.1', 'localhost'] INTERNAL_IPS = [ '127.0.0.1', diff --git a/src/static/images/fair_trade_stamp.png b/src/static/images/fair_trade_stamp.png index 9bc0f2e..d41c7dc 100644 Binary files a/src/static/images/fair_trade_stamp.png and b/src/static/images/fair_trade_stamp.png differ diff --git a/src/static/images/site_logo.svg b/src/static/images/site_logo.svg new file mode 100644 index 0000000..dc5c8d2 --- /dev/null +++ b/src/static/images/site_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/static/scripts/cookie.js b/src/static/scripts/cookie.js index fcc94bc..8e2378d 100644 --- a/src/static/scripts/cookie.js +++ b/src/static/scripts/cookie.js @@ -16,9 +16,9 @@ export function getCookie(name) { const twentyYears = 20 * 365 * 24 * 60 * 60 * 1000 -export function setCookie(name, value) { +export function setCookie(name, value, expiration=twentyYears) { const body = [ name, value ].map(encodeURIComponent).join("=") - const expires = new Date(Date.now() + twentyYears).toUTCString() + const expires = new Date(Date.now() + expiration).toUTCString() const cookie = `${body}; domain=; path=/; SameSite=Lax; expires=${expires}` document.cookie = cookie } diff --git a/src/static/scripts/index.js b/src/static/scripts/index.js new file mode 100644 index 0000000..e7820e6 --- /dev/null +++ b/src/static/scripts/index.js @@ -0,0 +1,37 @@ +import { getCookie, setCookie } from "./cookie.js" + +// Get the modal +const modal = document.querySelector(".modal-menu"); + +// Get the element that closes the modal +const closeBtn = document.querySelector(".close-modal"); + +const oneDay = 1 * 24 * 60 * 60 * 1000 + +// When the user clicks on (x), close the modal +closeBtn.addEventListener("click", event => { + modal.style.display = "none"; + setCookie('newsletter-modal', 'true', oneDay) +}) + +const scrollFunction = () => { + let modalDismissed = getCookie('newsletter-modal') + console.log(modalDismissed) + if (modalDismissed != 'true') { + if (document.body.scrollTop > 600 || document.documentElement.scrollTop > 600) { + modal.style.display = "block"; + } + } +} + +window.onscroll = () => { + scrollFunction(); +}; + +// When the user clicks anywhere outside of the modal, close it +window.addEventListener("click", event => { + if (event.target == modal) { + modal.style.display = "none"; + } + setCookie('newsletter-modal', 'true', oneDay) +}); diff --git a/src/static/scripts/payment.js b/src/static/scripts/payment.js index 1ba1e47..1c3345f 100644 --- a/src/static/scripts/payment.js +++ b/src/static/scripts/payment.js @@ -1,6 +1,6 @@ import { getCookie } from "./cookie.js" -let form = document.querySelector('form.order__form') +let form = document.querySelector('form') // Render the PayPal button into #paypal-button-container paypal.Buttons({ diff --git a/src/static/scripts/product_gallery.js b/src/static/scripts/product_gallery.js new file mode 100644 index 0000000..eecc3bd --- /dev/null +++ b/src/static/scripts/product_gallery.js @@ -0,0 +1,12 @@ +const gallery = document.querySelectorAll('.gallery__thumbnail') +const productImage = document.querySelector('.product__image') +let currentImg = document.querySelector('.gallery__thumbnail--focus') + +gallery.forEach(image => { + image.addEventListener('mouseover', event => { + currentImg.classList.remove('gallery__thumbnail--focus') + event.target.classList.add('gallery__thumbnail--focus') + currentImg = event.target + productImage.src = currentImg.src + }) +}) diff --git a/src/static/styles/main.css b/src/static/styles/main.css index 17e4494..b9990eb 100644 --- a/src/static/styles/main.css +++ b/src/static/styles/main.css @@ -1,9 +1,11 @@ :root { - --fg-color: #333; + --fg-color: #34201A; --bg-color: #f5f5f5; + --bg-alt-color: #eee5d3; --gray-color: #9d9d9d; --yellow-color: #f8a911; --yellow-alt-color: #ffce6f; + --yellow-dark-color: #b27606; --default-border: 2px solid var(--gray-color); } @@ -13,17 +15,22 @@ html { } body { - background: var(--bg-color); + background-color: var(--bg-color); font-family: 'Inter', sans-serif; font-weight: 400; - max-width: 1024px; - padding: 1rem; + padding: 0; margin: 0 auto; line-height: 1.75; color: var(--fg-color); } + +/* ========================================================================== + Typography + ========================================================================== */ + p { + margin-top: 0; margin-bottom: 1rem; } @@ -33,9 +40,9 @@ a { h1, h2, h3, h4, h5 { margin: 0; - font-family: 'Eczar', serif; font-weight: 700; line-height: 1.3; + margin-bottom: 1rem; } h1 { @@ -59,61 +66,61 @@ h5 { font-size: 1.2rem; } -small, .text_small { +small, .text-small { font-size: 0.833rem; } +.text-center { + text-align: center; +} - - - - - -button, -input, -optgroup, -select, -textarea { - color: inherit; +/* ========================================================================== + Tables + ========================================================================== */ +table { + border-collapse: collapse; + border-spacing: 0; font: inherit; - margin: 0; - max-width: 100%; - box-sizing: border-box; + width: 100%; + margin-bottom: 1rem; + border: 1px solid var(--gray-color); } -input { - outline: 0; -} - - - -label { - display: block; +td, +th { + font: inherit; text-align: left; - font-weight: 700; + font-size: 0.85em; + padding: 2px 10px; + border-bottom: 1px solid; + border-color: var(--gray-color); } -select { - text-align: left; - border: var(--default-border); - padding: 0.5em; - outline: 0; +th { + font-weight: bold; } +table a { + white-space: normal; +} + + +/* ========================================================================== + Forms + ========================================================================== */ input[type=text], input[type=email], input[type=number], input[type=date], input[type=password], -select[multiple=multiple], +select, textarea { - display: block; - text-align: left; color: var(--fg-color); border: var(--default-border); padding: 0.5rem; outline: 0; + box-sizing: border-box; } input:focus, @@ -122,7 +129,7 @@ textarea:focus { } select[multiple=multiple] { - height: 125px; + height: 8rem; } @@ -142,7 +149,6 @@ input[type=checkbox] + label { textarea { width: 100%; height: 6.25rem; - line-height: 1.45; } ::-webkit-input-placeholder { @@ -156,36 +162,36 @@ textarea { } -.action-button, +button, input[type=submit], -button { +.action-button { font-family: inherit; font-weight: bold; - font-size: 1rem; - color: var(--fg-color); + font-size: 1.5rem; text-decoration: none; text-transform: lowercase; font-variant: small-caps; + white-space: nowrap; + + color: var(--fg-color); background-color: var(--yellow-color); + padding: 0.25rem 1rem; border-radius: 0.2rem; border: none; + cursor: pointer; } +button, +input[type=submit]:hover, .action-button:hover { background-color: var(--yellow-alt-color); } -.action-button--large { - font-size: 2rem; -} -form input, -form select { - width: 100%; - box-sizing: border-box; -} + + @@ -196,249 +202,481 @@ figure { } img { box-sizing: border-box; -} - -.product__item { - display: grid; - grid-template-columns: 2fr 1fr; - gap: 0 2rem; -} - -.product__list-item button { - grid-column: 1/3; - align-self: end; -} - - - -.product__image { - /*object-fit: cover;*/ max-width: 100%; } -.product__form { - display: grid; - grid-template-columns: 1fr 3fr; - gap: 1rem; + +/* ========================================================================== + Base Layout + ========================================================================== */ +.site__header > nav, +main > article, +footer > section { + max-width: 1024px; + padding: 1rem; + margin: 0 auto; } -.product__form input[type=submit] { - grid-column: 2; - justify-self: end; + +/* ========================================================================== + Modal + ========================================================================== */ +.modal-menu { + display: none; + position: fixed; /* Stay in place */ + z-index: 1; /* Sit on top */ + left: 0; + top: 0; + width: 100%; /* Full width */ + height: 100%; /* Full height */ + overflow: auto; /* Enable scroll if needed */ + background-color: rgb(0,0,0); /* Fallback color */ + background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ } -.site__logo { - text-decoration: none; +/* Modal Content/Box */ +.modal-menu__content { + background-color: var(--bg-color); + margin: 25vh auto; + padding: 20px; + border: var(--default-border); + max-width: 40rem; } -.site__header div, -.site__header nav { + +.modal-menu__header { display: flex; justify-content: space-between; align-items: baseline; } -nav a { - text-transform: lowercase; - font-variant: small-caps; +/* The Close Button */ +.close-modal { + color: #aaa; + font-size: 2rem; + line-height: 0; font-weight: bold; - text-decoration: none; } -nav { - margin-bottom: 4rem; -} - -.site__copyright { - text-align: center; -} - -.keep_calm { - display: grid; - grid-template-columns: 1fr 2fr; - gap: 2rem; -} -.keep_calm__img { - max-width: 250px; -} - - -.site__cart { - display: flex; - align-items: center; - background-color: var(--yellow-color); - padding: 0.25rem 1rem; - text-decoration: none; - border-radius: 0.2rem; +.close-modal:hover, +.close-modal:focus { color: var(--fg-color); + text-decoration: none; cursor: pointer; } +/* ========================================================================== + Site + ========================================================================== */ +.site__ft-stamp { + max-width: 6rem; +} + +/* Site Header + ========================================================================== */ +.site__header { + background-color: var(--bg-alt-color); +} + +.site__logo > img { + height: 4rem; +} + +/* Site Nav + ========================================================================== */ + +.site__nav { + display: flex; + justify-content: space-between; + align-items: center; +} + +.site__logo, +.nav__main, +.nav__account { + margin-right: 1rem; +} + +@media screen and (max-width: 900px) { + .site__logo, + .nav__main, + .nav__account { + margin-right: 0; + } + .site__nav { + display: grid; + grid-template-columns: 2fr 0.5fr 0.5fr; + gap: 1rem; + } + .site__logo { + grid-column: 1; + } + .nav__main { + grid-column: 1/4; + grid-row: 2; + justify-content: space-between; + } + .nav__account { + grid-column: 2; + grid-row: 1; + } + .site__cart { + grid-column: 3; + grid-row: 1; + } +} + +@media screen and (max-width: 400px) { + .site__nav { + grid-template-columns: repeat(2, 1fr); + } + .site__logo { + grid-column: span 2; + justify-self: center; + } + .nav__main { + grid-column: 1/3; + grid-row: 3; + } + .nav__account { + grid-column: 1; + grid-row: 2; + } + .site__cart { + grid-column: 2; + grid-row: 2; + } +} + +.nav__list { + list-style: none; + margin: 0; + padding: 0; + display: flex; +} + +.nav__menu { + position: relative; +} + +.nav__link { + text-transform: lowercase; + font-variant: small-caps; + font-weight: bold; + text-decoration: none; + white-space: nowrap; +} + +.nav__list li:not(:last-child) { + margin-right: 1rem; +} + +.nav__list.menu { + flex-direction: column; +} + +.nav__dropdown { + list-style: none; + margin: 0; + padding: 0; + background-color: var(--bg-color); + border: var(--default-border); + padding: 0.5rem; + position: absolute; + top: 100%; + right: 0; + z-index: 1000; + display: none; +} + +@media screen and (max-width: 400px) { + .nav__dropdown { + left: 0; + right: unset; + } +} + +.nav__menu:hover .nav__dropdown { + display: flex; + flex-direction: column; +} + +/* Site Cart + ========================================================================== */ +.site__cart { + display: flex; + justify-content: center; + align-items: center; + + justify-self: end; + + font-family: inherit; + font-weight: bold; + text-decoration: none; + text-transform: lowercase; + font-variant: small-caps; + + color: var(--fg-color); + background-color: var(--yellow-color); + padding: 0.25rem 1rem; + border-radius: 0.2rem; + cursor: pointer; +} + +.cart__count { + font-size: 1rem; +} + .cart__icon { - width: 2rem; height: 2rem; } -.cart__length { - font-size: 1.5rem; - font-weight: bold; - font-family: 'Inter', sans-serif; + +/* ========================================================================== + Articles + ========================================================================== */ +article > header { + margin-bottom: 1rem; } -.cart__item { +/* Products + ========================================================================== */ +.product__list { display: grid; - grid-template-columns: 1fr 5fr; - gap: 1rem; - padding: 2rem 0; - border-bottom: 0.05rem solid var(--gray-color); + grid-template-columns: repeat(2, 1fr); + gap: 4rem; } -.cart__total_price { +.product__item { + text-decoration: none; + + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; +} + +.product__item h3 { + text-decoration: underline; +} + +@media screen and (max-width: 900px) { + .product__list { + grid-template-columns: 1fr; + gap: 6rem; + } + .product__figure { + grid-row: span 2; + } +} + +@media screen and (max-width: 600px) { + .product__item { + grid-template-columns: 1fr; + } + + .product__figure { + grid-row: 1; + justify-self: center; + } +} + + +/* Product Detail + ========================================================================== */ +.product__detail { + display: grid; + grid-template-columns: 0.25fr 2fr 1fr; + gap: 1rem; +} + +.gallery__thumbnail { + border: var(--default-border); + border-color: var(--bg-color); + cursor: pointer; + object-fit: cover; + aspect-ratio: 1/1; +} + +.gallery__image { + text-align: center; +} + +.gallery__thumbnail--focus { + border-color: var(--yellow-color); +} + +.product__form input, +.product__form select { + width: 100%; +} + +@media screen and (max-width: 700px) { + .product__detail { + grid-template-columns: 0.25fr 2fr; + } + + .product__info { + grid-column: span 2; + } +} + +@media screen and (max-width: 700px) { + .product__detail { + grid-template-columns: 1fr; + grid-template-rows: 4rem 1fr auto; + } + .product__gallery { + display: flex; + } + .gallery__thumbnail { + max-height: 4rem; + } + .gallery__thumbnail:not(:last-child) { + margin-right: 0.5rem; + } + .product__image { + max-height: 16rem; + } + .product__info { + grid-column: 1; + } +} + + +/* Shopping Cart + ========================================================================== */ +.cart__list { + margin-bottom: 2rem; +} +.cart__item { + padding: 1rem 0; + border-bottom: var(--default-border); + display: grid; + grid-template-columns: 1fr 3fr 1fr; + gap: 1rem; +} + +.cart__table-wrapper { + display: flex; + justify-content: flex-end; +} + +.cart__summary { text-align: right; +} +.cart__totals { + width: unset; font-size: 1.5rem; } -.cart__total { + +.cart__totals th, +.cart__totals td { + border: none; +} +.cart__totals th:first-child, +.cart__totals td:first-child { + text-align: right; + +} + +.cart__proceed { + text-align: right; display: flex; align-items: center; justify-content: flex-end; } -.coupon__form { - max-width: 16rem; - align-self: flex-end; +.cart__proceed > .action-button { + font-size: 1.75rem; } -.item__figure img { - vertical-align: middle; +.item__image { + max-height: 12rem; +} + +.item__price { + justify-self: end; +} + +.item__form p, +.coupon__form p { + display: flex; + align-items: center; +} + +.coupon__form p { + justify-content: flex-end; +} + +.item__form input[type=number] { + max-width: 6rem; +} + +.coupon__form input[type=text] { + max-width: 8rem; +} + +:is(.item__form, .coupon__form) label, +:is(.item__form, .coupon__form) input:not(:last-child) { + margin-right: 0.5rem; +} + +@media screen and (max-width: 500px) { + .cart__item { + grid-template-columns: 2fr 1fr; + } + + .cart__proceed { + flex-direction: column; + } + + .item__info { + grid-column: span 2; + grid-row: 2; + } + + .item__price { + grid-column: 2; + grid-row: 1; + } } -.product__list { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 8rem; + +/* Checkout / Shipping Address + ========================================================================== */ +.checkout__address-form input, +.checkout__address-form select { + display: block; + width: 100%; + max-width: 24rem; } -.product__list-item { - text-decoration: none; - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 1rem; +.checkout__address { + margin-bottom: 2rem; } -.product__list-item form { - grid-column: 1/3; -} - -.product__list-item figure { - margin: 0; - padding: 0; +#paypal-button-container { + max-width: 24rem; + margin-left: auto; } -.order__shipping { -} -.shipping__details { - margin-bottom: 3rem; - max-width: 32rem; -} - -.shipping__details input[type=submit] { - font-size: 1.25rem; - -} - - -.order__total { - margin: 3rem 0; - text-align: right; -} - +/* ========================================================================== + Footer + ========================================================================== */ footer { margin: 4rem 0 0; box-sizing: border-box; - border-top: var(--default-border); - padding: 2rem 0; -} - - - - - - - -.object__header { - display: flex; - align-items: baseline; - justify-content: space-between; - margin-bottom: 1rem; -} - -.object__list, -.object__panel { - background-color: white; - border-radius: 0.5rem; - margin-bottom: 2rem; -} - -.object__item { - display: grid; - grid-template-columns: repeat(5, 1fr); - gap: 1rem; - padding: 1rem; - border-bottom: 0.05rem solid var(--gray-color); - text-decoration: none; - align-items: center; -} - -.panel__item:last-child, -.object__item:last-child { - border-bottom: unset; - border-radius: 0 0 0.5rem 0.5rem; -} - -.object__item:hover { + padding: 1rem 0; background-color: var(--bg-alt-color); } -.panel__header { - font-weight: bold; - background-color: var(--bg-alt-color); - border-radius: 0.5rem 0.5rem 0 0; -} - -.panel__item { - padding: 1rem; - border-bottom: 0.05rem solid var(--gray-color); - text-decoration: none; -} - - -.product__detail { - display: grid; - grid-template-columns: 1fr 2fr; - gap: 2rem; -} - - -.user__emails { - margin-bottom: 4rem; -} - - - - - - -._form_1 { - margin: 0; -} -._form_1 div form { - margin: 1rem 0 !important; - padding: 0 !important; +footer > section { + text-align: center; } diff --git a/src/static/styles/normalize.css b/src/static/styles/normalize.css new file mode 100644 index 0000000..192eb9c --- /dev/null +++ b/src/static/styles/normalize.css @@ -0,0 +1,349 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} diff --git a/src/storefront/cart.py b/src/storefront/cart.py index 2a6ba01..906e10e 100644 --- a/src/storefront/cart.py +++ b/src/storefront/cart.py @@ -23,18 +23,15 @@ class Cart: cart = self.session[settings.CART_SESSION_ID] = {} self.cart = cart - def add(self, product, quantity=1, roast='', update_quantity=False): + def add(self, product, quantity=1, grind='', update_quantity=False): product_id = str(product.id) if product_id not in self.cart: self.cart[product_id] = { 'quantity': 0, - 'roast': roast, + 'grind': grind, 'price': str(product.price) } - elif product_id in self.cart: - self.cart[product_id].update({ - 'roast': roast, - }) + if update_quantity: self.cart[product_id]['quantity'] = quantity else: @@ -71,7 +68,7 @@ class Cart: def clear(self): del self.session[settings.CART_SESSION_ID] - del self.coupon_code + del self.session['coupon_code'] self.session.modified = True def build_order_params(self): @@ -97,7 +94,7 @@ class Cart: bulk_list = [OrderLine( order=order, product=item['product'], - customer_note=item['roast'], + customer_note=item['grind'], unit_price=item['price'], quantity=item['quantity'], tax_rate=2, diff --git a/src/storefront/forms.py b/src/storefront/forms.py index 524c351..b962d6a 100644 --- a/src/storefront/forms.py +++ b/src/storefront/forms.py @@ -19,7 +19,7 @@ class AddToCartForm(forms.Form): AEROPRESS = 'AeroPress' PERCOLATOR = 'Percolator' OTHER = 'Other' - ROAST_CHOICES = [ + GRIND_CHOICES = [ (WHOLE, 'Whole Beans'), (ESPRESSO, 'Espresso'), (CONE_DRIP, 'Cone Drip'), @@ -31,11 +31,34 @@ class AddToCartForm(forms.Form): (OTHER, 'Other (enter below)') ] - ONE_TIME = 'One-time purchase' - SUBSCRIBE = 'Subscribe' - PURCHASE_TYPE_CHOICES = [ - (ONE_TIME, 'One-time purchase'), - (SUBSCRIBE, 'Subscribe and save 10%'), + grind = forms.ChoiceField(choices=GRIND_CHOICES) + quantity = forms.IntegerField(min_value=1, max_value=20, initial=1) + +class UpdateCartItemForm(forms.Form): + quantity = forms.IntegerField(min_value=1, max_value=20, initial=1) + update = forms.BooleanField(required=False, initial=True, widget=forms.HiddenInput) + + +class AddToSubscriptionForm(forms.Form): + WHOLE = 'Whole Beans' + ESPRESSO = 'Espresso' + CONE_DRIP = 'Cone Drip' + BASKET_DRIP = 'Basket Drip' + FRENCH_PRESS = 'French Press' + STOVETOP_ESPRESSO = 'Stovetop Espresso (Moka Pot)' + AEROPRESS = 'AeroPress' + PERCOLATOR = 'Percolator' + OTHER = 'Other' + GRIND_CHOICES = [ + (WHOLE, 'Whole Beans'), + (ESPRESSO, 'Espresso'), + (CONE_DRIP, 'Cone Drip'), + (BASKET_DRIP, 'Basket Drip'), + (FRENCH_PRESS, 'French Press'), + (STOVETOP_ESPRESSO, 'Stovetop Espresso (Moka Pot)'), + (AEROPRESS, 'AeroPress'), + (PERCOLATOR, 'Percolator'), + (OTHER, 'Other (enter below)') ] SEVEN_DAYS = 7 @@ -48,10 +71,8 @@ class AddToCartForm(forms.Form): ] quantity = forms.IntegerField(min_value=1, initial=1) - roast = forms.ChoiceField(choices=ROAST_CHOICES) - purchase_type = forms.ChoiceField(choices=PURCHASE_TYPE_CHOICES, initial=ONE_TIME) + grind = forms.ChoiceField(choices=GRIND_CHOICES) schedule = forms.ChoiceField(choices=SCHEDULE_CHOICES) - update = forms.BooleanField(required=False, initial=False, widget=forms.HiddenInput) class AddressForm(forms.Form): @@ -83,7 +104,7 @@ class OrderCreateForm(forms.ModelForm): } class CouponApplyForm(forms.Form): - code = forms.CharField(label='Enter coupon') + code = forms.CharField(label='Coupon code') class ContactForm(forms.Form): GOOGLE = 'Google Search' diff --git a/src/storefront/templates/storefront/about.html b/src/storefront/templates/storefront/about.html index 6a01753..eb35732 100644 --- a/src/storefront/templates/storefront/about.html +++ b/src/storefront/templates/storefront/about.html @@ -3,10 +3,14 @@ {% block content %}

-

About PT Coffee

-
- -
+
+

About PT Coffee

+
+
+
+ +
+

We love coffee!

If you’ve found Port Townsend Coffee Roasting Co., you probably love coffee so much that you seek out the best tasting, Certified Fair Trade Organic coffees available.

@@ -27,7 +31,7 @@

We roast our coffee much more slowly than most roasters, especially coffee roasters in the Pacific Northwest. The slower roasting process allows for greater bean development. The bean is evenly roasted right to the center and our process mutes some of the acidic compounds, which smooths out the flavor.

Port Townsend Coffee is known for roasts that are darker than others available in the Northwest. Due to our roasting process, which emphasizes patience with the beans as well as air flow adjustments, the darker roasts are smooth and syrupy. Dark roasts from other companies can taste bitter, or slightly burnt due to the size and speed of the roast.

We have been buying beans from the same brokers for many years. They understand the flavor profiles we prefer and seek to accommodate our needs.

-
+
Fair Trade and Organic

We pay a steep premium for these beans, which are typically from smaller farms that are organized into co-ops. These farms take pride in their coffees, as the farmers make a living wage and their families are able to live in a healthier, more secure environment than farmers who grow a conventional coffee crop. The quality of our coffee is consistent, in part due to the quality of organic and fair trade beans.

Freshness and Storage
diff --git a/src/storefront/templates/storefront/address_form.html b/src/storefront/templates/storefront/address_form.html new file mode 100644 index 0000000..acec0f3 --- /dev/null +++ b/src/storefront/templates/storefront/address_form.html @@ -0,0 +1,19 @@ +{% extends 'base.html' %} + +{% block content %} +
+
+

← Back

+

Update Address

+
+
+ + {% csrf_token %} + {{ form.as_p }} +

+ +

+ +
+
+{% endblock %} diff --git a/src/storefront/templates/storefront/cart_detail.html b/src/storefront/templates/storefront/cart_detail.html index 8c349a9..60cd87c 100644 --- a/src/storefront/templates/storefront/cart_detail.html +++ b/src/storefront/templates/storefront/cart_detail.html @@ -2,51 +2,73 @@ {% block content %}
-

Shopping Cart

-
+
+

Shopping Cart

+
+
{% for item in cart %}
{% with product=item.product %}
- {{product.productphoto_set.first.image}} + {{product.productphoto_set.first.image}}
-
-

{{product.name}}
${{item.price}}

+
+

{{product.name}}

+

Grind: {{item.grind}}

+
+ {% csrf_token %} +

+ {{ item.update_quantity_form }} + +

+

Remove from cart

-
- {% csrf_token %} - {{ item.update_quantity_form }} - -
+
+
+

${{item.price}}

{% endwith %}
{% empty %} -

No items in cart yet.

+
+

No items in cart yet.

+
{% endfor %}
-
-
-
+
+

Cart Totals

+
+ {% csrf_token %} - {{ coupon_apply_form.as_p }}

+ {{ coupon_apply_form }}

+
+ + + + + + {% if cart.coupon %} + + + + + {% endif %} + + + + +
Subtotal${{cart.get_total_price|floatformat:"2"}}
Coupon{{cart.coupon.discount_value}} {{cart.coupon.get_discount_value_type_display}}
Total${{cart.get_total_price_after_discount|floatformat:"2"}}
+
+

+ Continue Shopping{% if cart|length > 0 %} or Proceed to Checkout{% endif %} +

-

- Subtotal: ${{cart.get_total_price|floatformat:"2"}}
- {% if cart.coupon %} - Coupon: {{cart.coupon.discount_value}} {{cart.coupon.get_discount_value_type_display}}
- {% endif %} - Cart total: ${{cart.get_total_price_after_discount|floatformat:"2"}} -

-

- Continue Shopping or Proceed to Checkout -

{% endblock %} diff --git a/src/storefront/templates/storefront/checkout_address.html b/src/storefront/templates/storefront/checkout_address.html index d5d1f4e..2a05980 100644 --- a/src/storefront/templates/storefront/checkout_address.html +++ b/src/storefront/templates/storefront/checkout_address.html @@ -3,16 +3,18 @@ {% block content %}
-

Checkout

-
-
-

Shipping Address

-
- {% csrf_token %} - {{form.as_p}} +
+

Checkout

+
+
+

Shipping Address

+ + {% csrf_token %} + {{form.as_p}} +

-

-
+

+
{% endblock %} diff --git a/src/storefront/templates/storefront/checkout_shipping.html b/src/storefront/templates/storefront/checkout_shipping.html deleted file mode 100644 index f529cf6..0000000 --- a/src/storefront/templates/storefront/checkout_shipping.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "base.html" %} -{% load static %} - -{% block content %} -
-

Checkout

-
-
-

Shipping Method

-
- {{form.as_p}} - -
-
-
-
-{% endblock %} diff --git a/src/storefront/templates/storefront/contact_form.html b/src/storefront/templates/storefront/contact_form.html index 8c892c5..a0faae2 100644 --- a/src/storefront/templates/storefront/contact_form.html +++ b/src/storefront/templates/storefront/contact_form.html @@ -2,12 +2,16 @@ {% block content %}
-

Contact PT Coffee

+
+

Contact us

+
{% csrf_token %} {{form.as_p}} - +

+ +

diff --git a/src/storefront/templates/storefront/customer_detail.html b/src/storefront/templates/storefront/customer_detail.html index 62f6d1f..c3a3acd 100644 --- a/src/storefront/templates/storefront/customer_detail.html +++ b/src/storefront/templates/storefront/customer_detail.html @@ -3,20 +3,18 @@ {% block content %}
-
+

{{customer.get_full_name}}

Edit profile
-
-
-

Info

-
-
+
+

Info

+
Email address
{{customer.email}}
Manage
-
+
Default shipping address
{% with shipping_address=customer.default_shipping_address %}
@@ -28,11 +26,10 @@ {% endif %} {{shipping_address.city}}, {{shipping_address.state}}, {{shipping_address.postal_code}}
- Edit {% endwith %}
-
- Other addresses
+
+

All addresses

{% for address in customer.addresses.all %}

@@ -44,7 +41,7 @@ {% endif %} {{address.city}}, {{address.state}}, {{address.postal_code}}
- Edit + Edit

{% empty %}

No other addresses.

@@ -52,21 +49,29 @@
{% with order_list=customer.orders.all %} -
-
- Order # - Date - Total -
- {% for order in order_list %} - - #{{order.pk}} - {{order.created_at|date:"D, M j Y"}} - ${{order.total_net_amount}} - - {% empty %} - No orders - {% endfor %} +

Your orders

+
+ + + + + + + + + + {% for order in order_list %} + + + + + + + {% empty %} + No orders + {% endfor %} + +
Order #DateTotal
#{{order.pk}}{{order.created_at|date:"M j, Y"}}${{order.total_net_amount}}See details →
{% endwith %}
diff --git a/src/storefront/templates/storefront/customer_form.html b/src/storefront/templates/storefront/customer_form.html index 4ef2901..db37b6c 100644 --- a/src/storefront/templates/storefront/customer_form.html +++ b/src/storefront/templates/storefront/customer_form.html @@ -1,14 +1,17 @@ {% extends "base.html" %} {% block content %} -
-

Update your profile

+
+
+

← Back

+

Update your profile

+
{% csrf_token %} {{form.as_p}} -

- or cancel +

+

diff --git a/src/storefront/templates/storefront/order_detail.html b/src/storefront/templates/storefront/order_detail.html new file mode 100644 index 0000000..195e5ec --- /dev/null +++ b/src/storefront/templates/storefront/order_detail.html @@ -0,0 +1,63 @@ +{% extends "base.html" %} +{% load static %} + +{% block content %} +
+

← Back

+
+

Order #{{order.pk}}

+

Placed on {{order.created_at|date:"M j, Y"}}

+
+
+ + + + + + + + + + + {% for item in order.lines.all %} + + {% with product=item.product %} + + + + + + {% endwith %} + + {% empty %} + + + + {% endfor %} + +
ProductQuantityPriceTotal
+ {{product.productphoto_set.first.image}} + {{product.name}}{{item.quantity}}${{product.price}}${{item.get_total}}
No items in order
+
+ +
+

Payment

+ + + + + + {% if order.coupon %} + + + + + {% endif %} + + + + +
Subtotal${{order.total_net_amount}}
Discount{{order.coupon.discount_value}} {{order.coupon.get_discount_value_type_display}}
Total${{order.get_total_price_after_discount}}
+
+
+{% endblock content %} diff --git a/src/storefront/templates/storefront/order_form.html b/src/storefront/templates/storefront/order_form.html index a29d6ec..d4e46d4 100644 --- a/src/storefront/templates/storefront/order_form.html +++ b/src/storefront/templates/storefront/order_form.html @@ -2,59 +2,73 @@ {% load static %} {% block head %} - + {% endblock %} {% block content %}
-

Checkout

-
-
-

Shipping Address

-
- {{shipping_address.first_name}} - {{shipping_address.last_name}}
- {{shipping_address.street_address_1}}
- {% if shipping_address.street_address_2 %} - {{shipping_address.street_address_2}}
- {% endif %} - {{shipping_address.city}}, {{shipping_address.state}}, {{shipping_address.postal_code}} -
- edit -
-
-

Cart Summary

- {% for item in cart %} -
- {% with product=item.product %} -
- {{item.quantity}} x - {{product.productphoto_set.first.image}} -
-
-

{{product.name}}
${{item.price}}

-

Grind options: {{item.customer_note}}

-
- {% endwith %} +
+

Checkout

+
+
+

Shipping address

+
+ {{shipping_address.first_name}} + {{shipping_address.last_name}}
+ {{shipping_address.street_address_1}}
+ {% if shipping_address.street_address_2 %} + {{shipping_address.street_address_2}}
+ {% endif %} + {{shipping_address.city}}, {{shipping_address.state}}, {{shipping_address.postal_code}} +
+ Change +
+
+

Review items

+ {% for item in cart %} +
+ {% with product=item.product %} +
+ {{product.productphoto_set.first.image}} +
+
+

{{product.name}}

+

Grind options: {{item.grind}}

+

Quantity: {{item.quantity}}

- {% endfor %} +
+

${{item.price}}

+
+ {% endwith %}
-
-
- {% csrf_token %} - {{form.as_p}} - {# #} -
-

- Subtotal: ${{cart.get_total_price|floatformat:"2"}}
+ {% endfor %} +

+
+

Order summary

+
+ {% csrf_token %} + {{form.as_p}} +
+
+ + + + + {% if cart.coupon %} - Coupon: {{cart.coupon.discount_value}} {{cart.coupon.get_discount_value_type_display}}
+ + + + {% endif %} - Cart total: ${{cart.get_total_price_after_discount|floatformat:"2"}} -

-
+ + + + +
Subtotal${{cart.get_total_price|floatformat:"2"}}
Coupon{{cart.coupon.discount_value}} {{cart.coupon.get_discount_value_type_display}}
Total${{cart.get_total_price_after_discount|floatformat:"2"}}
+
{% endblock %} diff --git a/src/storefront/templates/storefront/product_detail.html b/src/storefront/templates/storefront/product_detail.html index f2287fb..611c939 100644 --- a/src/storefront/templates/storefront/product_detail.html +++ b/src/storefront/templates/storefront/product_detail.html @@ -2,24 +2,30 @@ {% load static %} {% block head %} - + {% endblock %} {% block content %} -
-
- {{product.productphoto_set.first.image}} +
+ + -
+

{{product.name}}

{{product.description}}

+

${{product.price}}

{{product.weight.oz}} oz

-
+ {% csrf_token %} {{ form.as_p }}

- +

diff --git a/src/storefront/templates/storefront/product_list.html b/src/storefront/templates/storefront/product_list.html index 5229c94..f008430 100644 --- a/src/storefront/templates/storefront/product_list.html +++ b/src/storefront/templates/storefront/product_list.html @@ -2,18 +2,20 @@ {% block content %}
+
+

Coffee

+
{% for product in product_list %} - +
{{product.productphoto_set.first.image}}
-

{{ product.name }}


-

${{product.price}}

-

{{product.description}}

+

{{ product.name }}

+

{{product.description|truncatewords:20}}

+

${{product.price}} | {{product.weight.oz}} oz

-
{% endfor %}
diff --git a/src/storefront/urls.py b/src/storefront/urls.py index 1cfc1a1..4c505f8 100644 --- a/src/storefront/urls.py +++ b/src/storefront/urls.py @@ -12,6 +12,7 @@ urlpatterns = [ path('cart/', views.CartView.as_view(), name='cart-detail'), path('cart//add/', views.CartAddProductView.as_view(), name='cart-add'), + path('cart//update/', views.CartUpdateProductView.as_view(), name='cart-update'), path('cart//remove/', views.cart_remove_product_view, name='cart-remove'), path('coupon/apply/', views.CouponApplyView.as_view(), name='coupon-apply'), @@ -28,5 +29,9 @@ urlpatterns = [ path('', views.CustomerDetailView.as_view(), name='customer-detail'), path('update/', views.CustomerUpdateView.as_view(), name='customer-update'), # path('delete/', views.CustomerDeleteView.as_view(), name='customer-delete'), + + path('orders//', views.OrderDetailView.as_view(), name='order-detail'), + path('addresses//update/', views.AddressUpdateView.as_view(), name='address-update'), ])), + ] diff --git a/src/storefront/views.py b/src/storefront/views.py index 0eed505..32a9aae 100644 --- a/src/storefront/views.py +++ b/src/storefront/views.py @@ -23,10 +23,11 @@ from paypalcheckoutsdk.core import PayPalHttpClient, SandboxEnvironment from accounts.models import User, Address from accounts.utils import get_or_create_customer +from accounts.forms import AddressForm as AccountAddressForm, CustomerUpdateForm from core.models import Product, Order, Transaction, OrderLine, Coupon from core.forms import ShippingMethodForm -from .forms import AddToCartForm, OrderCreateForm, AddressForm, CouponApplyForm, ContactForm +from .forms import AddToCartForm, UpdateCartItemForm, OrderCreateForm, AddressForm, CouponApplyForm, ContactForm from .cart import Cart from .payments import CaptureOrder @@ -39,11 +40,9 @@ class CartView(TemplateView): context = super().get_context_data(**kwargs) cart = Cart(self.request) for item in cart: - item['update_quantity_form'] = AddToCartForm( + item['update_quantity_form'] = UpdateCartItemForm( initial={ 'quantity': item['quantity'], - 'roast': item['roast'], - 'update': True } ) context['cart'] = cart @@ -63,7 +62,30 @@ class CartAddProductView(SingleObjectMixin, FormView): if form.is_valid(): cart.add( product=self.get_object(), - roast=form.cleaned_data['roast'], + grind=form.cleaned_data['grind'], + quantity=form.cleaned_data['quantity'] + ) + return self.form_valid(form) + else: + return self.form_invalid(form) + + def form_valid(self, form): + return super().form_valid(form) + + +class CartUpdateProductView(SingleObjectMixin, FormView): + model = Product + form_class = UpdateCartItemForm + + def get_success_url(self): + return reverse('storefront:cart-detail') + + def post(self, request, *args, **kwargs): + cart = Cart(request) + form = self.get_form() + if form.is_valid(): + cart.add( + product=self.get_object(), quantity=form.cleaned_data['quantity'], update_quantity=form.cleaned_data['update'] ) @@ -181,6 +203,7 @@ class OrderCreateView(CreateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['shipping_address'] = self.request.session.get('shipping_address') + context['PAYPAL_CLIENT_ID'] = settings.PAYPAL_CLIENT_ID return context def form_valid(self, form): @@ -227,25 +250,46 @@ class PaymentCanceledView(TemplateView): template_name = 'storefront/payment_canceled.html' -class CustomerDetailView(DetailView): +class CustomerDetailView(LoginRequiredMixin, DetailView): model = User template_name = 'storefront/customer_detail.html' context_object_name = 'customer' -class CustomerUpdateView(UpdateView): +class CustomerUpdateView(LoginRequiredMixin, UpdateView): model = User template_name = 'storefront/customer_form.html' context_object_name = 'customer' - fields = ( - 'first_name', - 'last_name', - 'email', - 'default_shipping_address' - ) + form_class = CustomerUpdateForm def get_success_url(self): return reverse('storefront:customer-detail', kwargs={'pk': self.object.pk}) +class OrderDetailView(LoginRequiredMixin, DetailView): + model = Order + template_name = 'storefront/order_detail.html' + pk_url_kwarg = 'order_pk' + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['customer'] = User.objects.get(pk=self.kwargs['pk']) + return context + +class AddressUpdateView(LoginRequiredMixin, UpdateView): + model = Address + pk_url_kwarg = 'address_pk' + template_name = 'storefront/address_form.html' + form_class = AccountAddressForm + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['customer'] = User.objects.get(pk=self.kwargs['pk']) + return context + + def get_success_url(self): + return reverse('storefront:customer-detail',kwargs={'pk': self.kwargs['pk']}) + + + class AboutView(TemplateView): template_name = 'storefront/about.html' diff --git a/src/templates/account/email.html b/src/templates/account/email.html index a2bc362..9884d05 100644 --- a/src/templates/account/email.html +++ b/src/templates/account/email.html @@ -6,6 +6,7 @@ {% block content %}
+

← Back

{% trans "E-mail Addresses" %}

diff --git a/src/templates/base.html b/src/templates/base.html index 818312a..c2120b4 100644 --- a/src/templates/base.html +++ b/src/templates/base.html @@ -14,73 +14,77 @@ {% compress css %} + {% endcompress %} + {% block head %} {% endblock %} -
+ +