Updates and fixes
This commit is contained in:
parent
775df2501a
commit
5400002772
@ -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',
|
||||
)
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<h1><img src="{% static 'images/coupon.png' %}" alt=""> Coupon</h1>
|
||||
</header>
|
||||
<section class="coupon__detail object__panel">
|
||||
<form method="post">{% csrf_token %}
|
||||
<form method="post" class="panel__item">{% csrf_token %}
|
||||
<p>Are you sure you want to delete "{{ object }}"?</p>
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
<header class="object__header">
|
||||
<h1><img src="{% static 'images/coupon.png' %}" alt=""> {{ coupon.name }}</h1>
|
||||
<div class="object__menu">
|
||||
<a href="" class="action-button action-button--warning">Delete</a>
|
||||
<a href="" class="action-button">Edit</a>
|
||||
<a href="{% url 'dashboard:coupon-delete' coupon.pk %}" class="action-button action-button--warning">Delete</a>
|
||||
<a href="{% url 'dashboard:coupon-update' coupon.pk %}" class="action-button">Edit</a>
|
||||
</div>
|
||||
</header>
|
||||
<section class="product__detail object__panel">
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
{% block content %}
|
||||
<article class="product">
|
||||
<p><a href="{% url 'storefront:customer-detail' customer.pk %}">← Back</a></p>
|
||||
<header class="object__header">
|
||||
<h1>Update Customer</h1>
|
||||
</header>
|
||||
|
||||
@ -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',
|
||||
|
||||
27
src/static/images/site_logo.svg
Normal file
27
src/static/images/site_logo.svg
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 678 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;">
|
||||
<g transform="matrix(1,0,0,1,-5907.71,-3175.04)">
|
||||
<g transform="matrix(0.1989,0,0,0.200287,4811.02,2843.24)">
|
||||
<g>
|
||||
<g transform="matrix(2.63714,0,0,2.61886,5483.86,1392.53)">
|
||||
<g transform="matrix(-0.992546,0.121868,0.121868,0.992546,97.3127,159.801)">
|
||||
<ellipse cx="2.834" cy="-23.086" rx="18.086" ry="23.259" style="fill:none;stroke:rgb(165,30,33);stroke-width:6.4px;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,95.0702,115.154)">
|
||||
<path d="M0,45.049C0,45.049 -9.064,38.527 -3.117,19.662C2.83,0.798 -10.105,0 -10.105,0" style="fill:none;fill-rule:nonzero;stroke:rgb(165,30,33);stroke-width:6.4px;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,24.5511,131.024)">
|
||||
<path d="M0,77.434L32.057,45.377C32.057,45.377 43.453,21.797 24.635,0C24.635,0 1.01,20.485 23.081,54.353" style="fill:none;fill-rule:nonzero;stroke:rgb(165,30,33);stroke-width:6.4px;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,24.5425,164.997)">
|
||||
<path d="M0,43.453L32.057,11.396C32.057,11.396 55.637,0 77.434,18.818C77.434,18.818 56.949,42.443 23.081,20.372" style="fill:none;fill-rule:nonzero;stroke:rgb(165,30,33);stroke-width:6.4px;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(2.80799,0,0,2.78854,-9128.45,-2495.82)">
|
||||
<text x="5350.13px" y="1564.69px" style="font-family:'Vollkorn-ExtraBold', 'Vollkorn';font-weight:800;font-size:66.667px;fill:rgb(52,32,26);">P<tspan x="5392.06px 5429.86px 5462.13px 5486.93px 5498.73px 5537.26px 5574.4px 5626.13px 5670.86px 5701.6px 5735.73px 5779px 5819.86px 5832.2px 5877.13px 5915.4px 5952.13px 5981.86px 6006.66px 6030.66px 6075.4px 6112.06px 6124.4px 6166px 6203.8px 6269.6px 6312.33px 6349.06px 6390.66px " y="1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px 1564.69px ">ort Townsend Roasting Company</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@ -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;
|
||||
|
||||
}
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -3,12 +3,18 @@
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<h1>About PT Coffee</h1>
|
||||
<figure>
|
||||
<img src="{% static 'images/coffee_banner.jpeg' %}" alt="">
|
||||
</figure>
|
||||
<header class="object__header">
|
||||
<h1>About PT Coffee</h1>
|
||||
</header>
|
||||
<section>
|
||||
<h2>We love coffee!</h2>
|
||||
<figure>
|
||||
<img src="{% static 'images/coffee_banner.jpeg' %}" alt="">
|
||||
</figure>
|
||||
</section>
|
||||
<section>
|
||||
<header class="object__header">
|
||||
<h2>We love coffee!</h2>
|
||||
</header>
|
||||
<p><strong>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.</strong></p>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
|
||||
16
src/storefront/templates/storefront/address_form.html
Normal file
16
src/storefront/templates/storefront/address_form.html
Normal file
@ -0,0 +1,16 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<section>
|
||||
<p><a href="{% url 'storefront:customer-detail' user.pk %}">← Back</a></p>
|
||||
<h1>Update Address</h1>
|
||||
<form method="post" action="{% url 'storefront:address-update' customer.pk address.pk %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
|
||||
<input type="submit" value="Save changes" class="action-button">
|
||||
</form>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -2,20 +2,23 @@
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<h2>Shopping Cart</h2>
|
||||
<section>
|
||||
<section class="object__panel">
|
||||
<header class="panel__item panel__header">
|
||||
<h2>Shopping Cart</h2>
|
||||
</header>
|
||||
{% for item in cart %}
|
||||
<div class="cart__item">
|
||||
<div class="cart__item panel__item">
|
||||
{% with product=item.product %}
|
||||
<figure class="item__figure">
|
||||
<img class="product__image product__image--small" src="{{product.productphoto_set.first.image.url}}" alt="{{product.productphoto_set.first.image}}">
|
||||
</figure>
|
||||
<div class="item__details">
|
||||
<p>{{product.name}}<br> ${{item.price}}</p>
|
||||
<p>{{item.roast}}</p>
|
||||
<p>
|
||||
<a href="{% url 'storefront:cart-remove' product.pk %}">Remove from cart</a>
|
||||
</p>
|
||||
<form class="product__form" action="{% url 'storefront:cart-add' product.pk %}" method="POST">
|
||||
<form class="product__form" action="{% url 'storefront:cart-update' product.pk %}" method="POST">
|
||||
{% csrf_token %}
|
||||
{{ item.update_quantity_form }}
|
||||
<input type="submit" value="Update">
|
||||
@ -27,26 +30,31 @@
|
||||
<p>No items in cart yet.</p>
|
||||
{% endfor %}
|
||||
</section>
|
||||
<section>
|
||||
<div class="cart__total">
|
||||
<form action="{% url 'storefront:coupon-apply' %}" method="post" class="coupon__form">
|
||||
{% csrf_token %}
|
||||
{{ coupon_apply_form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Apply" class="action-button">
|
||||
</p>
|
||||
</form>
|
||||
<section class="object__panel">
|
||||
<header class="panel__item panel__header">
|
||||
<h4>Cart Totals</h4>
|
||||
</header>
|
||||
<div class="panel__item">
|
||||
<div class="cart__total">
|
||||
<form action="{% url 'storefront:coupon-apply' %}" method="post" class="coupon__form">
|
||||
{% csrf_token %}
|
||||
{{ coupon_apply_form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Apply" class="action-button">
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
<p class="cart__total_price">
|
||||
<span class="">Subtotal: ${{cart.get_total_price|floatformat:"2"}}</span><br>
|
||||
{% if cart.coupon %}
|
||||
<span class="">Coupon: {{cart.coupon.discount_value}} {{cart.coupon.get_discount_value_type_display}}</span><br>
|
||||
{% endif %}
|
||||
<span><strong>Cart total: ${{cart.get_total_price_after_discount|floatformat:"2"}}</strong></span>
|
||||
</p>
|
||||
<p class="cart__total">
|
||||
<a href="{% url 'storefront:product-list' %}">Continue Shopping</a> or <a class="action-button action-button--large" href="{% url 'storefront:checkout-address' %}">Proceed to Checkout</a>
|
||||
</p>
|
||||
</div>
|
||||
<p class="cart__total_price">
|
||||
<span class="">Subtotal: ${{cart.get_total_price|floatformat:"2"}}</span><br>
|
||||
{% if cart.coupon %}
|
||||
<span class="">Coupon: {{cart.coupon.discount_value}} {{cart.coupon.get_discount_value_type_display}}</span><br>
|
||||
{% endif %}
|
||||
<span class="cart__total_price">Cart total: <strong>${{cart.get_total_price_after_discount|floatformat:"2"}}</strong></span>
|
||||
</p>
|
||||
<p class="cart__total">
|
||||
<a href="{% url 'storefront:product-list' %}">Continue Shopping</a> or <a class="action-button action-button--large" href="{% url 'storefront:checkout-address' %}">Proceed to Checkout</a>
|
||||
</p>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
|
||||
@ -3,9 +3,11 @@
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<h2>Checkout</h2>
|
||||
<section class="order__shipping">
|
||||
<div class="shipping__details">
|
||||
<header class="object__header">
|
||||
<h2>Checkout</h2>
|
||||
</header>
|
||||
<section class="order__shipping object__panel">
|
||||
<div class="shipping__details panel__item">
|
||||
<h3>Shipping Address</h3>
|
||||
<form action="" method="POST" class="address__form">
|
||||
{% csrf_token %}
|
||||
|
||||
@ -28,11 +28,10 @@
|
||||
{% endif %}
|
||||
{{shipping_address.city}}, {{shipping_address.state}}, {{shipping_address.postal_code}}
|
||||
</address>
|
||||
<a href="address-update">Edit</a>
|
||||
{% endwith %}
|
||||
</div>
|
||||
<div class="panel__item">
|
||||
<strong>Other addresses</strong><br>
|
||||
<p><strong>All addresses</strong></p>
|
||||
{% for address in customer.addresses.all %}
|
||||
<p>
|
||||
<address>
|
||||
@ -44,7 +43,7 @@
|
||||
{% endif %}
|
||||
{{address.city}}, {{address.state}}, {{address.postal_code}}
|
||||
</address>
|
||||
<a href="address-update">Edit</a>
|
||||
<a href="{% url 'storefront:address-update' customer.pk address.pk %}">Edit</a>
|
||||
</p>
|
||||
{% empty %}
|
||||
<p>No other addresses.</p>
|
||||
@ -53,15 +52,15 @@
|
||||
</section>
|
||||
{% with order_list=customer.orders.all %}
|
||||
<section class="object__list">
|
||||
<div class="object__item panel__header" href="order-detail">
|
||||
<div class="object__item panel__header object__item--col3" href="order-detail">
|
||||
<span>Order #</span>
|
||||
<span>Date</span>
|
||||
<span>Total</span>
|
||||
</div>
|
||||
{% for order in order_list %}
|
||||
<a class="object__item" href="">
|
||||
<a class="object__item object__item--col3" href="{% url 'storefront:order-detail' customer.pk order.pk %}">
|
||||
<span>#{{order.pk}}</span>
|
||||
<span>{{order.created_at|date:"D, M j Y"}}</span>
|
||||
<span>{{order.created_at|date:"M j, Y"}}</span>
|
||||
<span>${{order.total_net_amount}}</span>
|
||||
</a>
|
||||
{% empty %}
|
||||
|
||||
@ -2,13 +2,14 @@
|
||||
|
||||
{% block content %}
|
||||
<article class="product">
|
||||
<p><a href="{% url 'storefront:customer-detail' customer.pk %}">← Back</a></p>
|
||||
<h1>Update your profile</h1>
|
||||
<section>
|
||||
<form method="POST" action="{% url 'storefront:customer-update' customer.pk %}">
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<p class="form__submit">
|
||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'storefront:customer-detail' customer.pk %}">cancel</a>
|
||||
<input class="action-button" type="submit" value="Save changes">
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
50
src/storefront/templates/storefront/order_detail.html
Normal file
50
src/storefront/templates/storefront/order_detail.html
Normal file
@ -0,0 +1,50 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<p><a href="{% url 'storefront:customer-detail' customer.pk %}">← Back</a></p>
|
||||
<header class="object__header">
|
||||
<h1><img src="{% static 'images/box.png' %}" alt=""> Order #{{order.pk}}</h1>
|
||||
</header>
|
||||
<section class="object__list">
|
||||
<div class="object__item panel__header object__item--col5">
|
||||
<span>Product</span>
|
||||
<span></span>
|
||||
<span>Quantity</span>
|
||||
<span>Price</span>
|
||||
<span>Total</span>
|
||||
</div>
|
||||
{% for item in order.lines.all %}
|
||||
<div class="object__item object__item--col5">
|
||||
{% with product=item.product %}
|
||||
<figure class="item__figure">
|
||||
<img class="product__image product__image--small" src="{{product.productphoto_set.first.image.url}}" alt="{{product.productphoto_set.first.image}}">
|
||||
</figure>
|
||||
<span><strong>{{product.name}}</strong></span>
|
||||
<span>{{item.quantity}}</span>
|
||||
<span>${{product.price}}</span>
|
||||
<span>${{item.get_total}}</span>
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class="object__item">No items in order yet.</p>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
<section class="object__panel">
|
||||
<div class="object__item panel__header">
|
||||
<h4>Payment</h4>
|
||||
</div>
|
||||
<div class="panel__item">
|
||||
<p>
|
||||
<span>Subtotal: {{order.total_net_amount}}</span><br>
|
||||
{% if order.coupon %}
|
||||
<span>Discount: {{order.coupon.discount_value}} {{order.coupon.get_discount_value_type_display}}</span><br>
|
||||
{% endif %}
|
||||
<strong>Total: {{order.get_total_price_after_discount}}</strong>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock content %}
|
||||
@ -8,9 +8,11 @@
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<h2>Checkout</h2>
|
||||
<section class="order__details">
|
||||
<div class="order__shipping">
|
||||
<header class="object__header">
|
||||
<h2>Checkout</h2>
|
||||
</header>
|
||||
<section class="object__panel">
|
||||
<div class="order__shipping panel__item">
|
||||
<h3>Shipping Address</h3>
|
||||
<address>
|
||||
{{shipping_address.first_name}}
|
||||
@ -23,24 +25,25 @@
|
||||
</address>
|
||||
<a class="action-button" href="{% url 'storefront:checkout-address' %}">edit</a>
|
||||
</div>
|
||||
<div class="order__list">
|
||||
<div class="order__list panel__item">
|
||||
<h3>Cart Summary</h3>
|
||||
{% for item in cart %}
|
||||
<div class="cart__item">
|
||||
{% with product=item.product %}
|
||||
<figure class="item__figure">
|
||||
{{item.quantity}} x
|
||||
<img class="product__image product__image--small" src="{{product.productphoto_set.first.image.url}}" alt="{{product.productphoto_set.first.image}}">
|
||||
</figure>
|
||||
<div class="item__details">
|
||||
<p>{{product.name}}<br> ${{item.price}}</p>
|
||||
<p><strong>Grind options</strong>: {{item.customer_note}}</p>
|
||||
<p><strong>Quantity</strong>: {{item.quantity}}</p>
|
||||
<p><strong>Grind options</strong>: {{item.roast}}</p>
|
||||
</div>
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="order__total">
|
||||
<div class="order__total panel__item">
|
||||
<h3>Order Totals</h3>
|
||||
<form action="" method="POST" class="order__form">
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
|
||||
@ -17,11 +17,17 @@
|
||||
<p>{{product.weight.oz}} oz</p>
|
||||
<form method="post" action="{% url 'storefront:cart-add' product.pk %}">
|
||||
{% csrf_token %}
|
||||
<h3>One-time purchase</h3>
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Add to cart" class="action-button">
|
||||
</p>
|
||||
</form>
|
||||
<br><hr><br>
|
||||
<form action="">
|
||||
<h3>Subscribe and save 10%</h3>
|
||||
</form>
|
||||
Subscriptions
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
|
||||
@ -9,9 +9,10 @@
|
||||
<img class="product__image" src="{{product.productphoto_set.first.image.url}}" alt="{{product.productphoto_set.first.image}}">
|
||||
</figure>
|
||||
<div>
|
||||
<h3>{{ product.name }}</h3><br>
|
||||
<h3>$<strong>{{product.price}}</strong></h3>
|
||||
<h3>{{ product.name }}</h3>
|
||||
<p>{{product.description}}</p>
|
||||
<p>$<strong>{{product.price}}</strong></p>
|
||||
<p>{{product.weight.oz}} oz</p>
|
||||
</div>
|
||||
<button class="action-button">View product</button>
|
||||
</a>
|
||||
|
||||
@ -12,6 +12,7 @@ urlpatterns = [
|
||||
|
||||
path('cart/', views.CartView.as_view(), name='cart-detail'),
|
||||
path('cart/<int:pk>/add/', views.CartAddProductView.as_view(), name='cart-add'),
|
||||
path('cart/<int:pk>/update/', views.CartUpdateProductView.as_view(), name='cart-update'),
|
||||
path('cart/<int:pk>/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/<int:order_pk>/', views.OrderDetailView.as_view(), name='order-detail'),
|
||||
path('addresses/<int:address_pk>/update/', views.AddressUpdateView.as_view(), name='address-update'),
|
||||
])),
|
||||
|
||||
]
|
||||
|
||||
@ -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'
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<p><a href="{% url 'storefront:customer-detail' user.pk %}">← Back</a></p>
|
||||
<section>
|
||||
<h1>{% trans "E-mail Addresses" %}</h1>
|
||||
</section>
|
||||
|
||||
@ -25,19 +25,19 @@
|
||||
<body>
|
||||
<header class="site__header">
|
||||
<div>
|
||||
<h1><a class="site__logo" href="{% url 'storefront:product-list' %}">Port Townsend Coffee</a></h1>
|
||||
<a href="{% url 'storefront:product-list' %}"><img class="site__logo" src="{% static 'images/site_logo.svg' %}" alt=""></a>
|
||||
{% if user.is_authenticated %}
|
||||
<p>
|
||||
<span>
|
||||
Hi <a href="{% url 'storefront:customer-detail' user.pk %}">{{user.first_name}} {{user.last_name}}</a>!
|
||||
</p>
|
||||
</span>
|
||||
{% if user.is_staff %}
|
||||
<p><a href="{% url 'dashboard:home' %}">Dashboard</a></p>
|
||||
<span><a href="{% url 'dashboard:home' %}">Dashboard</a></span>
|
||||
{% endif %}
|
||||
<p><a href="{% url 'account_logout' %}">Log Out</a></p>
|
||||
<span><a href="{% url 'account_logout' %}">Log Out</a></span>
|
||||
{% else %}
|
||||
<p>
|
||||
<span>
|
||||
<a href="{% url 'account_login' %}">Log In</a> | <a href="{% url 'account_signup' %}">Sign Up</a>
|
||||
</p>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<nav>
|
||||
@ -54,11 +54,6 @@
|
||||
</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<aside>
|
||||
{% block aside %}
|
||||
{% endblock %}
|
||||
</aside>
|
||||
<main>
|
||||
{% block content %}
|
||||
{% endblock content %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user