sandwitches 2.3.3__py3-none-any.whl → 2.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sandwitches/admin.py +10 -2
- sandwitches/forms.py +66 -6
- sandwitches/migrations/0012_rename_is_community_made_historicalrecipe_is_approved_and_more.py +22 -0
- sandwitches/migrations/0013_cartitem.py +55 -0
- sandwitches/migrations/0014_ensure_groups_exist.py +22 -0
- sandwitches/migrations/0015_order_completed_alter_order_status_and_more.py +56 -0
- sandwitches/models.py +35 -2
- sandwitches/templates/admin/admin_base.html +4 -0
- sandwitches/templates/admin/dashboard.html +1 -0
- sandwitches/templates/admin/partials/order_rows.html +1 -1
- sandwitches/templates/admin/recipe_approval_list.html +56 -0
- sandwitches/templates/admin/recipe_form.html +115 -20
- sandwitches/templates/admin/recipe_list.html +1 -1
- sandwitches/templates/cart.html +102 -0
- sandwitches/templates/community.html +111 -14
- sandwitches/templates/components/navbar.html +6 -0
- sandwitches/templates/components/recipe_header.html +3 -3
- sandwitches/templates/order_detail.html +68 -0
- sandwitches/templates/partials/recipe_list.html +11 -3
- sandwitches/templates/profile.html +90 -0
- sandwitches/urls.py +13 -0
- sandwitches/views.py +204 -17
- {sandwitches-2.3.3.dist-info → sandwitches-2.4.1.dist-info}/METADATA +1 -1
- {sandwitches-2.3.3.dist-info → sandwitches-2.4.1.dist-info}/RECORD +25 -18
- {sandwitches-2.3.3.dist-info → sandwitches-2.4.1.dist-info}/WHEEL +0 -0
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
</div>
|
|
89
89
|
</td>
|
|
90
90
|
<td class="min center-align">
|
|
91
|
-
{% if recipe.
|
|
91
|
+
{% if recipe.is_approved %}
|
|
92
92
|
<i class="primary-text">check_circle</i>
|
|
93
93
|
{% else %}
|
|
94
94
|
<a href="{% url 'admin_recipe_approve' recipe.pk %}" class="button tiny primary round" onclick="event.stopPropagation();">
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{% extends "base_beer.html" %}
|
|
2
|
+
{% load i18n static %}
|
|
3
|
+
|
|
4
|
+
{% block title %}{% trans "Your Cart" %}{% endblock %}
|
|
5
|
+
|
|
6
|
+
{% block content %}
|
|
7
|
+
<main class="responsive">
|
|
8
|
+
<div class="large-space"></div>
|
|
9
|
+
<div class="row align-center">
|
|
10
|
+
<i class="extra">shopping_cart</i>
|
|
11
|
+
<h4 class="max">{% trans "Your Shopping Cart" %}</h4>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="space"></div>
|
|
14
|
+
|
|
15
|
+
{% if cart_items %}
|
|
16
|
+
<div class="grid">
|
|
17
|
+
<div class="s12 m8">
|
|
18
|
+
<div class="padding border round surface">
|
|
19
|
+
<table class="border striped no-space">
|
|
20
|
+
<thead>
|
|
21
|
+
<tr>
|
|
22
|
+
<th class="min">{% trans "Item" %}</th>
|
|
23
|
+
<th class="max">{% trans "Description" %}</th>
|
|
24
|
+
<th class="min">{% trans "Quantity" %}</th>
|
|
25
|
+
<th class="min">{% trans "Price" %}</th>
|
|
26
|
+
<th class="min"></th>
|
|
27
|
+
</tr>
|
|
28
|
+
</thead>
|
|
29
|
+
<tbody>
|
|
30
|
+
{% for item in cart_items %}
|
|
31
|
+
<tr>
|
|
32
|
+
<td class="min">
|
|
33
|
+
{% if item.recipe.image %}
|
|
34
|
+
<img src="{{ item.recipe.image_thumbnail.url }}" class="circle small">
|
|
35
|
+
{% else %}
|
|
36
|
+
<i class="circle small gray1">restaurant</i>
|
|
37
|
+
{% endif %}
|
|
38
|
+
</td>
|
|
39
|
+
<td class="max">
|
|
40
|
+
<a href="{% url 'recipe_detail' item.recipe.slug %}" class="bold">{{ item.recipe.title }}</a>
|
|
41
|
+
</td>
|
|
42
|
+
<td class="min">
|
|
43
|
+
<form action="{% url 'update_cart_quantity' item.pk %}" method="post" class="row no-space align-center">
|
|
44
|
+
{% csrf_token %}
|
|
45
|
+
<div class="field border round small no-margin" style="width: 80px;">
|
|
46
|
+
<input type="number" name="quantity" value="{{ item.quantity }}" min="1" onchange="this.form.submit()">
|
|
47
|
+
</div>
|
|
48
|
+
</form>
|
|
49
|
+
</td>
|
|
50
|
+
<td class="min no-wrap">{{ item.total_price }} €</td>
|
|
51
|
+
<td class="min">
|
|
52
|
+
<a href="{% url 'remove_from_cart' item.pk %}" class="button circle transparent">
|
|
53
|
+
<i>delete</i>
|
|
54
|
+
</a>
|
|
55
|
+
</td>
|
|
56
|
+
</tr>
|
|
57
|
+
{% endfor %}
|
|
58
|
+
</tbody>
|
|
59
|
+
</table>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="s12 m4">
|
|
63
|
+
<article class="round padding border primary-container">
|
|
64
|
+
<h6 class="bold">{% trans "Summary" %}</h6>
|
|
65
|
+
<div class="row mt-1">
|
|
66
|
+
<div class="max">{% trans "Subtotal" %}</div>
|
|
67
|
+
<div class="bold">{{ total }} €</div>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="divider mt-1 mb-1"></div>
|
|
70
|
+
<div class="row">
|
|
71
|
+
<div class="max bold">{% trans "Total" %}</div>
|
|
72
|
+
<div class="bold large-text">{{ total }} €</div>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="space"></div>
|
|
75
|
+
<form action="{% url 'checkout_cart' %}" method="post">
|
|
76
|
+
{% csrf_token %}
|
|
77
|
+
<button type="submit" class="button primary round extend">
|
|
78
|
+
<i>check</i>
|
|
79
|
+
<span>{% trans "Checkout" %}</span>
|
|
80
|
+
</button>
|
|
81
|
+
</form>
|
|
82
|
+
</article>
|
|
83
|
+
<div class="space"></div>
|
|
84
|
+
<a href="{% url 'index' %}" class="button transparent extend">
|
|
85
|
+
<i>arrow_back</i>
|
|
86
|
+
<span>{% trans "Continue Shopping" %}</span>
|
|
87
|
+
</a>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
{% else %}
|
|
91
|
+
<div class="center-align padding">
|
|
92
|
+
<i class="extra gray-text">shopping_cart_off</i>
|
|
93
|
+
<h5 class="gray-text">{% trans "Your cart is empty." %}</h5>
|
|
94
|
+
<div class="space"></div>
|
|
95
|
+
<a href="{% url 'index' %}" class="button primary round">
|
|
96
|
+
<span>{% trans "Start Shopping" %}</span>
|
|
97
|
+
</a>
|
|
98
|
+
</div>
|
|
99
|
+
{% endif %}
|
|
100
|
+
<div class="large-space"></div>
|
|
101
|
+
</main>
|
|
102
|
+
{% endblock %}
|
|
@@ -4,6 +4,15 @@
|
|
|
4
4
|
{% block title %}{% trans "Community" %}{% endblock %}
|
|
5
5
|
|
|
6
6
|
{% block content %}
|
|
7
|
+
<style>
|
|
8
|
+
.cropper-container {
|
|
9
|
+
max-height: 70vh;
|
|
10
|
+
}
|
|
11
|
+
#cropper-image {
|
|
12
|
+
display: block;
|
|
13
|
+
max-width: 100%;
|
|
14
|
+
}
|
|
15
|
+
</style>
|
|
7
16
|
<main class="responsive">
|
|
8
17
|
<div class="large-space"></div>
|
|
9
18
|
<article class="round s12 m10 l8 offset-m1 offset-l2 elevate">
|
|
@@ -15,6 +24,7 @@
|
|
|
15
24
|
|
|
16
25
|
<form method="post" enctype="multipart/form-data">
|
|
17
26
|
{% csrf_token %}
|
|
27
|
+
{{ form.image_data }}
|
|
18
28
|
|
|
19
29
|
<div class="grid">
|
|
20
30
|
<div class="s12">
|
|
@@ -53,15 +63,21 @@
|
|
|
53
63
|
|
|
54
64
|
<!-- Image Upload with Preview -->
|
|
55
65
|
<div class="s12 m6">
|
|
56
|
-
<div class="field label border round">
|
|
66
|
+
<div class="field file label border round">
|
|
67
|
+
<input type="text" readonly>
|
|
57
68
|
{{ form.image }}
|
|
58
69
|
<label>{% trans "Image" %}</label>
|
|
70
|
+
<i>publish</i>
|
|
59
71
|
</div>
|
|
60
72
|
</div>
|
|
61
73
|
<div class="s12 m6 center-align relative">
|
|
62
|
-
<div class="padding border round dashed surface-variant" style="min-height: 150px; display: flex; align-items: center; justify-content: center;">
|
|
63
|
-
<img id="image-preview" src="{% if recipe.image %}{{ recipe.image.url }}{% endif %}" class="responsive round" style="max-height: 200px; {% if not recipe.image %}display:none;{% endif %}">
|
|
74
|
+
<div class="padding border round dashed surface-variant" style="min-height: 150px; display: flex; align-items: center; justify-content: center; flex-direction: column;">
|
|
75
|
+
<img id="image-preview" src="{% if recipe.image %}{{ recipe.image.url }}{% endif %}" class="responsive round mb-1" style="max-height: 200px; {% if not recipe.image %}display:none;{% endif %}">
|
|
64
76
|
<span id="image-placeholder" class="gray-text" {% if recipe.image %}style="display:none;"{% endif %}>{% trans "Image Preview" %}</span>
|
|
77
|
+
<button type="button" id="edit-image-btn" class="button transparent border round" style="display: none;" onclick="openCropper()">
|
|
78
|
+
<i>crop_rotate</i>
|
|
79
|
+
<span>{% trans "Edit Image" %}</span>
|
|
80
|
+
</button>
|
|
65
81
|
</div>
|
|
66
82
|
</div>
|
|
67
83
|
|
|
@@ -101,6 +117,41 @@
|
|
|
101
117
|
</div>
|
|
102
118
|
</article>
|
|
103
119
|
|
|
120
|
+
<dialog id="cropper-dialog" class="large">
|
|
121
|
+
<div class="padding">
|
|
122
|
+
<h5 class="bold mb-1">{% trans "Edit Image" %}</h5>
|
|
123
|
+
<div class="cropper-container mb-1">
|
|
124
|
+
<img id="cropper-image" src="">
|
|
125
|
+
</div>
|
|
126
|
+
<div class="row scroll no-space border round mb-1">
|
|
127
|
+
<button type="button" class="button transparent max" onclick="cropper.rotate(-90)" title="{% trans 'Rotate Left' %}">
|
|
128
|
+
<i>rotate_left</i>
|
|
129
|
+
</button>
|
|
130
|
+
<button type="button" class="button transparent max" onclick="cropper.rotate(90)" title="{% trans 'Rotate Right' %}">
|
|
131
|
+
<i>rotate_right</i>
|
|
132
|
+
</button>
|
|
133
|
+
<div class="divider vertical"></div>
|
|
134
|
+
<button type="button" class="button transparent max" onclick="cropper.scaleX(-cropper.getData().scaleX || -1)" title="{% trans 'Flip Horizontal' %}">
|
|
135
|
+
<i>flip</i>
|
|
136
|
+
</button>
|
|
137
|
+
<button type="button" class="button transparent max" onclick="cropper.scaleY(-cropper.getData().scaleY || -1)" title="{% trans 'Flip Vertical' %}">
|
|
138
|
+
<i>flip</i>
|
|
139
|
+
</button>
|
|
140
|
+
<div class="divider vertical"></div>
|
|
141
|
+
<button type="button" class="button transparent max" onclick="cropper.setAspectRatio(1)" title="{% trans '1:1' %}">1:1</button>
|
|
142
|
+
<button type="button" class="button transparent max" onclick="cropper.setAspectRatio(4/3)" title="{% trans '4:3' %}">4:3</button>
|
|
143
|
+
<button type="button" class="button transparent max" onclick="cropper.setAspectRatio(16/9)" title="{% trans '16:9' %}">16:9</button>
|
|
144
|
+
<button type="button" class="button transparent max" onclick="cropper.setAspectRatio(NaN)" title="{% trans 'Free' %}">
|
|
145
|
+
<i>crop_free</i>
|
|
146
|
+
</button>
|
|
147
|
+
</div>
|
|
148
|
+
<nav class="right-align">
|
|
149
|
+
<button type="button" class="button transparent" onclick="ui('#cropper-dialog')">{% trans "Cancel" %}</button>
|
|
150
|
+
<button type="button" class="button primary" onclick="applyCrop()">{% trans "Apply" %}</button>
|
|
151
|
+
</nav>
|
|
152
|
+
</div>
|
|
153
|
+
</dialog>
|
|
154
|
+
|
|
104
155
|
|
|
105
156
|
<div class="large-space"></div>
|
|
106
157
|
|
|
@@ -117,21 +168,67 @@
|
|
|
117
168
|
|
|
118
169
|
{% block page_scripts %}
|
|
119
170
|
<script>
|
|
120
|
-
|
|
121
|
-
|
|
171
|
+
let cropper;
|
|
172
|
+
const imageInput = document.querySelector('input[type="file"]');
|
|
173
|
+
const imagePreview = document.getElementById('image-preview');
|
|
174
|
+
const imagePlaceholder = document.getElementById('image-placeholder');
|
|
175
|
+
const editImageBtn = document.getElementById('edit-image-btn');
|
|
176
|
+
const imageDataInput = document.getElementsByName('image_data')[0];
|
|
177
|
+
const cropperImage = document.getElementById('cropper-image');
|
|
178
|
+
|
|
179
|
+
function openCropper() {
|
|
180
|
+
if (!imagePreview.src || imagePreview.src === window.location.href) return;
|
|
181
|
+
cropperImage.src = imagePreview.src;
|
|
182
|
+
ui('#cropper-dialog');
|
|
183
|
+
|
|
184
|
+
if (cropper) {
|
|
185
|
+
cropper.destroy();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
setTimeout(() => {
|
|
189
|
+
cropper = new Cropper(cropperImage, {
|
|
190
|
+
viewMode: 1,
|
|
191
|
+
autoCropArea: 1,
|
|
192
|
+
responsive: true,
|
|
193
|
+
restore: false,
|
|
194
|
+
checkCrossOrigin: true,
|
|
195
|
+
guides: true,
|
|
196
|
+
center: true,
|
|
197
|
+
highlight: false,
|
|
198
|
+
cropBoxMovable: true,
|
|
199
|
+
cropBoxResizable: true,
|
|
200
|
+
toggleDragModeOnDblclick: false,
|
|
201
|
+
});
|
|
202
|
+
}, 100);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function applyCrop() {
|
|
206
|
+
const canvas = cropper.getCroppedCanvas({
|
|
207
|
+
maxWidth: 2000,
|
|
208
|
+
maxHeight: 2000,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const croppedData = canvas.toDataURL('image/jpeg', 0.9);
|
|
212
|
+
imagePreview.src = croppedData;
|
|
213
|
+
imagePreview.style.display = 'block';
|
|
214
|
+
if (imagePlaceholder) imagePlaceholder.style.display = 'none';
|
|
215
|
+
if (editImageBtn) editImageBtn.style.display = 'inline-flex';
|
|
216
|
+
imageDataInput.value = croppedData;
|
|
217
|
+
|
|
218
|
+
ui('#cropper-dialog');
|
|
219
|
+
}
|
|
220
|
+
|
|
122
221
|
if (imageInput) {
|
|
123
222
|
imageInput.onchange = function (evt) {
|
|
124
|
-
|
|
125
|
-
files = tgt.files;
|
|
126
|
-
|
|
223
|
+
const files = evt.target.files;
|
|
127
224
|
if (FileReader && files && files.length) {
|
|
128
|
-
|
|
225
|
+
const fr = new FileReader();
|
|
129
226
|
fr.onload = function () {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
227
|
+
imagePreview.src = fr.result;
|
|
228
|
+
imagePreview.style.display = 'block';
|
|
229
|
+
if (imagePlaceholder) imagePlaceholder.style.display = 'none';
|
|
230
|
+
if (editImageBtn) editImageBtn.style.display = 'inline-flex';
|
|
231
|
+
openCropper();
|
|
135
232
|
}
|
|
136
233
|
fr.readAsDataURL(files[0]);
|
|
137
234
|
}
|
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
</button>
|
|
16
16
|
|
|
17
17
|
{% if user.is_authenticated %}
|
|
18
|
+
<a href="{% url 'view_cart' %}" class="button circle transparent">
|
|
19
|
+
<i>shopping_cart</i>
|
|
20
|
+
{% if user.cart_items.exists %}
|
|
21
|
+
<badge class="red">{{ user.cart_items.count }}</badge>
|
|
22
|
+
{% endif %}
|
|
23
|
+
</a>
|
|
18
24
|
<a href="{% url 'user_profile' %}">
|
|
19
25
|
{% if user.avatar %}
|
|
20
26
|
<img src="{{ user.avatar.url }}" class="circle">
|
|
@@ -37,11 +37,11 @@
|
|
|
37
37
|
<i class="primary-text">euro_symbol</i>
|
|
38
38
|
<h5 class="bold ml-1">{{ recipe.price }}</h5>
|
|
39
39
|
{% if user.is_authenticated %}
|
|
40
|
-
<form action="{% url '
|
|
40
|
+
<form action="{% url 'add_to_cart' recipe.pk %}" method="post" class="ml-2">
|
|
41
41
|
{% csrf_token %}
|
|
42
42
|
<button type="submit" class="button primary round">
|
|
43
|
-
<i>
|
|
44
|
-
<span>{% trans "
|
|
43
|
+
<i>add_shopping_cart</i>
|
|
44
|
+
<span>{% trans "Add to Cart" %}</span>
|
|
45
45
|
</button>
|
|
46
46
|
</form>
|
|
47
47
|
{% endif %}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{% extends "base_beer.html" %}
|
|
2
|
+
{% load static i18n %}
|
|
3
|
+
|
|
4
|
+
{% block title %}{% trans "Order Details" %} #{{ order.id }}{% endblock %}
|
|
5
|
+
|
|
6
|
+
{% block content %}
|
|
7
|
+
<div class="large-space"></div>
|
|
8
|
+
|
|
9
|
+
<div class="grid">
|
|
10
|
+
<div class="s12 m10 l8 xl6 middle-align center-align" style="margin: 0 auto;">
|
|
11
|
+
<article class="round elevate left-align">
|
|
12
|
+
<div class="padding">
|
|
13
|
+
<nav>
|
|
14
|
+
<a href="{% url 'user_profile' %}" class="button transparent circle">
|
|
15
|
+
<i>arrow_back</i>
|
|
16
|
+
</a>
|
|
17
|
+
<h5 class="max">{% trans "Order" %} #{{ order.id }}</h5>
|
|
18
|
+
<span class="chip {% if order.status == 'PENDING' %}surface-variant{% elif order.status == 'COMPLETED' %}primary{% elif order.status == 'CANCELLED' %}error{% else %}secondary{% endif %}">
|
|
19
|
+
{{ order.get_status_display }}
|
|
20
|
+
</span>
|
|
21
|
+
</nav>
|
|
22
|
+
<div class="divider"></div>
|
|
23
|
+
|
|
24
|
+
<div class="grid">
|
|
25
|
+
<div class="s12 m6">
|
|
26
|
+
<p class="small-text">{% trans "Date Ordered" %}</p>
|
|
27
|
+
<p>{{ order.created_at|date:"d F Y, H:i" }}</p>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="s12 m6">
|
|
30
|
+
<p class="small-text">{% trans "Last Update" %}</p>
|
|
31
|
+
<p>{{ order.updated_at|date:"d F Y, H:i" }}</p>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div class="divider"></div>
|
|
36
|
+
|
|
37
|
+
<h6>{% trans "Item Details" %}</h6>
|
|
38
|
+
<div class="row">
|
|
39
|
+
<div class="max">
|
|
40
|
+
<a href="{% url 'recipe_detail' order.recipe.slug %}" class="bold primary-text">{{ order.recipe.title }}</a>
|
|
41
|
+
<p class="small-text">{{ order.recipe.description|truncatewords:20 }}</p>
|
|
42
|
+
</div>
|
|
43
|
+
<div class="min">
|
|
44
|
+
<span class="bold">{{ order.total_price }} €</span>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
{% if order.recipe.image %}
|
|
49
|
+
<div class="space"></div>
|
|
50
|
+
<img src="{{ order.recipe.image.url }}" class="responsive round border" alt="{{ order.recipe.title }}">
|
|
51
|
+
{% endif %}
|
|
52
|
+
|
|
53
|
+
<div class="divider"></div>
|
|
54
|
+
|
|
55
|
+
<div class="row">
|
|
56
|
+
<div class="max text-right">
|
|
57
|
+
<span class="bold">{% trans "Total" %}</span>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="min">
|
|
60
|
+
<span class="bold text-primary">{{ order.total_price }} €</span>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
</div>
|
|
65
|
+
</article>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
{% endblock %}
|
|
@@ -72,11 +72,19 @@
|
|
|
72
72
|
</div>
|
|
73
73
|
</div>
|
|
74
74
|
|
|
75
|
-
<div class="padding pt-0">
|
|
76
|
-
<a href="{% url 'recipe_detail' recipe.slug %}" class="button fill round
|
|
77
|
-
<span>{% trans 'View
|
|
75
|
+
<div class="padding pt-0 row no-space">
|
|
76
|
+
<a href="{% url 'recipe_detail' recipe.slug %}" class="button fill round max">
|
|
77
|
+
<span>{% trans 'View' %}</span>
|
|
78
78
|
<i class="suffix">arrow_forward</i>
|
|
79
79
|
</a>
|
|
80
|
+
{% if user.is_authenticated and recipe.price %}
|
|
81
|
+
<form action="{% url 'add_to_cart' recipe.pk %}" method="post" class="ml-1">
|
|
82
|
+
{% csrf_token %}
|
|
83
|
+
<button type="submit" class="button circle primary" title="{% trans 'Add to Cart' %}">
|
|
84
|
+
<i>add_shopping_cart</i>
|
|
85
|
+
</button>
|
|
86
|
+
</form>
|
|
87
|
+
{% endif %}
|
|
80
88
|
</div>
|
|
81
89
|
</article>
|
|
82
90
|
</div>
|
|
@@ -90,6 +90,96 @@
|
|
|
90
90
|
|
|
91
91
|
</form>
|
|
92
92
|
</article>
|
|
93
|
+
|
|
94
|
+
<div class="large-space"></div>
|
|
95
|
+
|
|
96
|
+
<h4 class="center-align primary-text">{% trans "Order History" %}</h4>
|
|
97
|
+
|
|
98
|
+
<form method="get" class="row no-wrap middle-align">
|
|
99
|
+
<div class="field label border round small">
|
|
100
|
+
<select name="status" onchange="this.form.submit()">
|
|
101
|
+
<option value="">{% trans "All Statuses" %}</option>
|
|
102
|
+
{% for code, label in status_choices %}
|
|
103
|
+
<option value="{{ code }}" {% if current_status == code %}selected{% endif %}>{{ label }}</option>
|
|
104
|
+
{% endfor %}
|
|
105
|
+
</select>
|
|
106
|
+
<label>{% trans "Filter by Status" %}</label>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="space"></div>
|
|
109
|
+
<div class="field label border round small">
|
|
110
|
+
<select name="sort" onchange="this.form.submit()">
|
|
111
|
+
<option value="date_desc" {% if current_sort == 'date_desc' %}selected{% endif %}>{% trans "Newest First" %}</option>
|
|
112
|
+
<option value="date_asc" {% if current_sort == 'date_asc' %}selected{% endif %}>{% trans "Oldest First" %}</option>
|
|
113
|
+
<option value="price_desc" {% if current_sort == 'price_desc' %}selected{% endif %}>{% trans "Price: High to Low" %}</option>
|
|
114
|
+
<option value="price_asc" {% if current_sort == 'price_asc' %}selected{% endif %}>{% trans "Price: Low to High" %}</option>
|
|
115
|
+
</select>
|
|
116
|
+
<label>{% trans "Sort by" %}</label>
|
|
117
|
+
</div>
|
|
118
|
+
</form>
|
|
119
|
+
|
|
120
|
+
{% if orders %}
|
|
121
|
+
<div class="padding border round surface left-align">
|
|
122
|
+
<table class="border striped">
|
|
123
|
+
<thead>
|
|
124
|
+
<tr>
|
|
125
|
+
<th class="min">#</th>
|
|
126
|
+
<th class="max">{% trans "Recipe" %}</th>
|
|
127
|
+
<th class="min">{% trans "Date" %}</th>
|
|
128
|
+
<th class="min">{% trans "Status" %}</th>
|
|
129
|
+
<th class="min">{% trans "Price" %}</th>
|
|
130
|
+
<th class="min"></th>
|
|
131
|
+
</tr>
|
|
132
|
+
</thead>
|
|
133
|
+
<tbody>
|
|
134
|
+
{% for order in orders %}
|
|
135
|
+
<tr>
|
|
136
|
+
<td>{{ order.id }}</td>
|
|
137
|
+
<td>
|
|
138
|
+
<a href="{% url 'recipe_detail' order.recipe.slug %}">{{ order.recipe.title }}</a>
|
|
139
|
+
</td>
|
|
140
|
+
<td class="no-wrap">{{ order.created_at|date:"d/m/Y" }}</td>
|
|
141
|
+
<td>
|
|
142
|
+
<span class="chip tiny {% if order.status == 'PENDING' %}surface-variant{% elif order.status == 'COMPLETED' %}primary{% elif order.status == 'CANCELLED' %}error{% else %}secondary{% endif %}">
|
|
143
|
+
{{ order.get_status_display }}
|
|
144
|
+
</span>
|
|
145
|
+
</td>
|
|
146
|
+
<td class="no-wrap">{{ order.total_price }} €</td>
|
|
147
|
+
<td>
|
|
148
|
+
<a href="{% url 'user_order_detail' order.id %}" class="button circle transparent small">
|
|
149
|
+
<i>visibility</i>
|
|
150
|
+
</a>
|
|
151
|
+
</td>
|
|
152
|
+
</tr>
|
|
153
|
+
{% endfor %}
|
|
154
|
+
</tbody>
|
|
155
|
+
</table>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
{% if orders.paginator.num_pages > 1 %}
|
|
159
|
+
<div class="center-align padding">
|
|
160
|
+
<nav class="row">
|
|
161
|
+
{% if orders.has_previous %}
|
|
162
|
+
<a href="?page={{ orders.previous_page_number }}{% if current_status %}&status={{ current_status }}{% endif %}{% if current_sort %}&sort={{ current_sort }}{% endif %}" class="button transparent circle"><i>chevron_left</i></a>
|
|
163
|
+
{% else %}
|
|
164
|
+
<button class="button transparent circle" disabled><i>chevron_left</i></button>
|
|
165
|
+
{% endif %}
|
|
166
|
+
|
|
167
|
+
<span class="padding">{{ orders.number }} / {{ orders.paginator.num_pages }}</span>
|
|
168
|
+
|
|
169
|
+
{% if orders.has_next %}
|
|
170
|
+
<a href="?page={{ orders.next_page_number }}{% if current_status %}&status={{ current_status }}{% endif %}{% if current_sort %}&sort={{ current_sort }}{% endif %}" class="button transparent circle"><i>chevron_right</i></a>
|
|
171
|
+
{% else %}
|
|
172
|
+
<button class="button transparent circle" disabled><i>chevron_right</i></button>
|
|
173
|
+
{% endif %}
|
|
174
|
+
</nav>
|
|
175
|
+
</div>
|
|
176
|
+
{% endif %}
|
|
177
|
+
|
|
178
|
+
{% else %}
|
|
179
|
+
<div class="padding border round surface">
|
|
180
|
+
<p class="center-align">{% trans "No previous orders found." %}</p>
|
|
181
|
+
</div>
|
|
182
|
+
{% endif %}
|
|
93
183
|
</div>
|
|
94
184
|
</div>
|
|
95
185
|
{% endblock %}
|
sandwitches/urls.py
CHANGED
|
@@ -34,11 +34,19 @@ urlpatterns = [
|
|
|
34
34
|
path("login/", views.CustomLoginView.as_view(), name="login"),
|
|
35
35
|
path("logout/", LogoutView.as_view(next_page="index"), name="logout"),
|
|
36
36
|
path("profile/", views.user_profile, name="user_profile"),
|
|
37
|
+
path("orders/<int:pk>/", views.user_order_detail, name="user_order_detail"),
|
|
37
38
|
path("community/", views.community, name="community"),
|
|
38
39
|
path("admin/", admin.site.urls),
|
|
39
40
|
path("api/", api.urls),
|
|
40
41
|
path("media/<path:file_path>", views.media, name="media"),
|
|
41
42
|
path("favorites/", views.favorites, name="favorites"),
|
|
43
|
+
path("cart/", views.view_cart, name="view_cart"),
|
|
44
|
+
path("cart/add/<int:pk>/", views.add_to_cart, name="add_to_cart"),
|
|
45
|
+
path("cart/remove/<int:pk>/", views.remove_from_cart, name="remove_from_cart"),
|
|
46
|
+
path(
|
|
47
|
+
"cart/update/<int:pk>/", views.update_cart_quantity, name="update_cart_quantity"
|
|
48
|
+
),
|
|
49
|
+
path("cart/checkout/", views.checkout_cart, name="checkout_cart"),
|
|
42
50
|
path("", views.index, name="index"),
|
|
43
51
|
path("feeds/latest/", LatestRecipesFeed(), name="latest_recipes_feed"),
|
|
44
52
|
path(
|
|
@@ -54,6 +62,11 @@ urlpatterns += i18n_patterns(
|
|
|
54
62
|
path("recipes/<int:pk>/favorite/", views.toggle_favorite, name="toggle_favorite"),
|
|
55
63
|
path("dashboard/", views.admin_dashboard, name="admin_dashboard"),
|
|
56
64
|
path("dashboard/recipes/", views.admin_recipe_list, name="admin_recipe_list"),
|
|
65
|
+
path(
|
|
66
|
+
"dashboard/approvals/",
|
|
67
|
+
views.admin_recipe_approval_list,
|
|
68
|
+
name="admin_recipe_approval_list",
|
|
69
|
+
),
|
|
57
70
|
path("dashboard/recipes/add/", views.admin_recipe_add, name="admin_recipe_add"),
|
|
58
71
|
path(
|
|
59
72
|
"dashboard/recipes/<int:pk>/edit/",
|