From 540000277240f720e62a990c1e45b5caabdf6085 Mon Sep 17 00:00:00 2001 From: Nathan Chapman Date: Sun, 3 Apr 2022 09:56:12 -0600 Subject: [PATCH] Updates and fixes --- src/accounts/forms.py | 30 ++++++++- src/core/models.py | 12 ++++ .../dashboard/coupon_confirm_delete.html | 2 +- .../templates/dashboard/coupon_detail.html | 4 +- .../templates/dashboard/customer_form.html | 1 + src/dashboard/views.py | 2 +- src/static/images/site_logo.svg | 27 ++++++++ src/static/styles/main.css | 35 +++++++--- src/storefront/cart.py | 7 +- src/storefront/forms.py | 35 ++++++++-- .../templates/storefront/about.html | 16 +++-- .../templates/storefront/address_form.html | 16 +++++ .../templates/storefront/cart_detail.html | 54 ++++++++------- .../storefront/checkout_address.html | 8 ++- .../templates/storefront/customer_detail.html | 11 ++- .../templates/storefront/customer_form.html | 3 +- .../templates/storefront/order_detail.html | 50 ++++++++++++++ .../templates/storefront/order_form.html | 17 +++-- .../templates/storefront/product_detail.html | 6 ++ .../templates/storefront/product_list.html | 5 +- src/storefront/urls.py | 5 ++ src/storefront/views.py | 67 +++++++++++++++---- src/templates/account/email.html | 1 + src/templates/base.html | 19 ++---- 24 files changed, 332 insertions(+), 101 deletions(-) create mode 100644 src/static/images/site_logo.svg create mode 100644 src/storefront/templates/storefront/address_form.html create mode 100644 src/storefront/templates/storefront/order_detail.html 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/views.py b/src/dashboard/views.py index 29d798a..5525621 100644 --- a/src/dashboard/views.py +++ b/src/dashboard/views.py @@ -279,7 +279,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/static/images/site_logo.svg b/src/static/images/site_logo.svg new file mode 100644 index 0000000..fe1303e --- /dev/null +++ b/src/static/images/site_logo.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + Port Townsend Roasting Company + + + + + diff --git a/src/static/styles/main.css b/src/static/styles/main.css index 17e4494..4d4d669 100644 --- a/src/static/styles/main.css +++ b/src/static/styles/main.css @@ -1,5 +1,5 @@ :root { - --fg-color: #333; + --fg-color: #34201A; --bg-color: #f5f5f5; --gray-color: #9d9d9d; --yellow-color: #f8a911; @@ -33,7 +33,6 @@ a { h1, h2, h3, h4, h5 { margin: 0; - font-family: 'Eczar', serif; font-weight: 700; line-height: 1.3; } @@ -63,6 +62,10 @@ small, .text_small { font-size: 0.833rem; } +.text-center { + text-align: center; +} + @@ -196,6 +199,7 @@ figure { } img { box-sizing: border-box; + max-width: 100%; } .product__item { @@ -228,7 +232,8 @@ img { } .site__logo { - text-decoration: none; + height: 3rem; + vertical-align: middle; } .site__header div, @@ -238,6 +243,13 @@ img { align-items: baseline; } +@media (max-width: 911px) { + .site__header div { + flex-direction: column; + justify-content: center; + } +} + nav a { text-transform: lowercase; font-variant: small-caps; @@ -288,12 +300,17 @@ nav { .cart__item { display: grid; - grid-template-columns: 1fr 5fr; + grid-template-columns: 1fr 1.5fr; gap: 1rem; padding: 2rem 0; border-bottom: 0.05rem solid var(--gray-color); } +.cart__item:last-child { + margin-bottom: 2rem; + border-bottom: none; +} + .cart__total_price { text-align: right; font-size: 1.5rem; @@ -337,10 +354,6 @@ nav { } -.order__shipping { -} - - .shipping__details { margin-bottom: 3rem; max-width: 32rem; @@ -353,7 +366,7 @@ nav { .order__total { - margin: 3rem 0; + /*margin: 3rem 0;*/ text-align: right; } @@ -437,8 +450,8 @@ footer { ._form_1 { margin: 0; } -._form_1 div form { - margin: 1rem 0 !important; +#_form_62486B0492FDE_ { + margin: 1.7rem 0 !important; padding: 0 !important; } diff --git a/src/storefront/cart.py b/src/storefront/cart.py index 2a6ba01..08ce2dd 100644 --- a/src/storefront/cart.py +++ b/src/storefront/cart.py @@ -31,10 +31,7 @@ class Cart: 'roast': roast, '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[self.coupon_code] self.session.modified = True def build_order_params(self): diff --git a/src/storefront/forms.py b/src/storefront/forms.py index 524c351..0582874 100644 --- a/src/storefront/forms.py +++ b/src/storefront/forms.py @@ -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%'), + quantity = forms.IntegerField(min_value=1, initial=1) + roast = forms.ChoiceField(choices=ROAST_CHOICES) + +class UpdateCartItemForm(forms.Form): + quantity = forms.IntegerField(min_value=1, 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' + ROAST_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 @@ -49,9 +72,7 @@ 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) schedule = forms.ChoiceField(choices=SCHEDULE_CHOICES) - update = forms.BooleanField(required=False, initial=False, widget=forms.HiddenInput) class AddressForm(forms.Form): diff --git a/src/storefront/templates/storefront/about.html b/src/storefront/templates/storefront/about.html index 6a01753..77c238b 100644 --- a/src/storefront/templates/storefront/about.html +++ b/src/storefront/templates/storefront/about.html @@ -3,12 +3,18 @@ {% block content %}
-

About PT Coffee

-
- -
+
+

About PT Coffee

+
-

We love 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.

You’ve probably been around coffee for years, perhaps starting with Specialty Coffees in the 1980’s, and know your way around fairly well. How and where coffee is grown and harvested, how it’s roasted, how to brew it. You value fair, guaranteed wages for growers and sustainable stewardship of the land where it’s grown.

No matter how much you drink, if you’re like us, great coffee is an important perk in your life. You are not alone in this… coffee is the one of the world’s most heavily traded commodities. In addition to the impact it has as a crop on the economies of producing countries, the cafe and coffee house industry touches millions of lives everyday, worldwide, as well.

diff --git a/src/storefront/templates/storefront/address_form.html b/src/storefront/templates/storefront/address_form.html new file mode 100644 index 0000000..ac38b54 --- /dev/null +++ b/src/storefront/templates/storefront/address_form.html @@ -0,0 +1,16 @@ +{% 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..1aae45d 100644 --- a/src/storefront/templates/storefront/cart_detail.html +++ b/src/storefront/templates/storefront/cart_detail.html @@ -2,20 +2,23 @@ {% block content %}
-

Shopping Cart

-
+
+
+

Shopping Cart

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

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

+

{{item.roast}}

Remove from cart

-
+ {% csrf_token %} {{ item.update_quantity_form }} @@ -27,26 +30,31 @@

No items in cart yet.

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

- -

- +
+
+

Cart Totals

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

+ +

+
+
+

+ 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 +

-

- 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..ef7ca16 100644 --- a/src/storefront/templates/storefront/checkout_address.html +++ b/src/storefront/templates/storefront/checkout_address.html @@ -3,9 +3,11 @@ {% block content %}
-

Checkout

-
-
+
+

Checkout

+
+
+

Shipping Address

{% csrf_token %} diff --git a/src/storefront/templates/storefront/customer_detail.html b/src/storefront/templates/storefront/customer_detail.html index 62f6d1f..6069a1e 100644 --- a/src/storefront/templates/storefront/customer_detail.html +++ b/src/storefront/templates/storefront/customer_detail.html @@ -28,11 +28,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 +43,7 @@ {% endif %} {{address.city}}, {{address.state}}, {{address.postal_code}}
- Edit + Edit

{% empty %}

No other addresses.

@@ -53,15 +52,15 @@
{% 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.created_at|date:"M j, Y"}} ${{order.total_net_amount}} {% empty %} diff --git a/src/storefront/templates/storefront/customer_form.html b/src/storefront/templates/storefront/customer_form.html index 4ef2901..37680d0 100644 --- a/src/storefront/templates/storefront/customer_form.html +++ b/src/storefront/templates/storefront/customer_form.html @@ -2,13 +2,14 @@ {% block content %}
+

← 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..a2bbe6c --- /dev/null +++ b/src/storefront/templates/storefront/order_detail.html @@ -0,0 +1,50 @@ +{% extends "base.html" %} +{% load static %} + +{% block content %} +
+

← Back

+
+

Order #{{order.pk}}

+
+
+
+ Product + + Quantity + Price + Total +
+ {% for item in order.lines.all %} +
+ {% with product=item.product %} +
+ {{product.productphoto_set.first.image}} +
+ {{product.name}} + {{item.quantity}} + ${{product.price}} + ${{item.get_total}} + {% endwith %} +
+ {% empty %} +

No items in order yet.

+ {% endfor %} +
+ +
+
+

Payment

+
+
+

+ Subtotal: {{order.total_net_amount}}
+ {% if order.coupon %} + Discount: {{order.coupon.discount_value}} {{order.coupon.get_discount_value_type_display}}
+ {% endif %} + 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..0fc9f7a 100644 --- a/src/storefront/templates/storefront/order_form.html +++ b/src/storefront/templates/storefront/order_form.html @@ -8,9 +8,11 @@ {% block content %}
-

Checkout

-
-
+
+

Checkout

+
+
+

Shipping Address

{{shipping_address.first_name}} @@ -23,24 +25,25 @@
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}}

+

Quantity: {{item.quantity}}

+

Grind options: {{item.roast}}

{% endwith %}
{% endfor %}
-
+
+

Order Totals

{% csrf_token %} {{form.as_p}} diff --git a/src/storefront/templates/storefront/product_detail.html b/src/storefront/templates/storefront/product_detail.html index f2287fb..724e8e3 100644 --- a/src/storefront/templates/storefront/product_detail.html +++ b/src/storefront/templates/storefront/product_detail.html @@ -17,11 +17,17 @@

{{product.weight.oz}} oz

{% csrf_token %} +

One-time purchase

{{ form.as_p }}

+


+
+

Subscribe and save 10%

+
+ Subscriptions
{% endblock %} diff --git a/src/storefront/templates/storefront/product_list.html b/src/storefront/templates/storefront/product_list.html index 5229c94..1d6b6b0 100644 --- a/src/storefront/templates/storefront/product_list.html +++ b/src/storefront/templates/storefront/product_list.html @@ -9,9 +9,10 @@ {{product.productphoto_set.first.image}}
-

{{ product.name }}


-

${{product.price}}

+

{{ product.name }}

{{product.description}}

+

${{product.price}}

+

{{product.weight.oz}} oz

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..bff7fb3 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 @@ -64,6 +63,29 @@ class CartAddProductView(SingleObjectMixin, FormView): cart.add( product=self.get_object(), roast=form.cleaned_data['roast'], + 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'] ) @@ -227,25 +249,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..7abba39 100644 --- a/src/templates/base.html +++ b/src/templates/base.html @@ -25,19 +25,19 @@ - -
{% block content %} {% endblock content %}