sandwitches 2.5.2__py3-none-any.whl → 2.5.3__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.
@@ -26,8 +26,7 @@
26
26
  <i>menu</i>
27
27
  </button>
28
28
  <a href="{% url 'admin_dashboard' %}" class="row align-center">
29
- <img src="{% static 'icons/icon.svg' %}" class="circle small">
30
- <h6 class="max">{% trans "Sandwitches Admin" %} | {% block admin_title %}{% endblock %}</h6>
29
+ <img src="{% static 'icons/icon.svg' %}" class="circle">
31
30
  </a>
32
31
  <div class="max"></div>
33
32
 
@@ -45,48 +44,52 @@
45
44
  <h5 class="max">{% trans "Admin" %}</h5>
46
45
  </header>
47
46
  <div class="space"></div>
48
- <a href="{% url 'admin_dashboard' %}" class="{% if request.resolver_match.url_name == 'admin_dashboard' %}active{% endif %}">
47
+ <a href="{% url 'admin_dashboard' %}" class="padding {% if request.resolver_match.url_name == 'admin_dashboard' %}active{% endif %}">
49
48
  <i>dashboard</i>
50
- <span>{% trans "Dashboard" %}</span>
49
+ <span class="large-text">{% trans "Dashboard" %}</span>
51
50
  </a>
52
- <a href="{% url 'admin_recipe_list' %}" class="{% if request.resolver_match.url_name == 'admin_recipe_list' %}active{% endif %}">
51
+ <a href="{% url 'admin_recipe_list' %}" class="padding {% if request.resolver_match.url_name == 'admin_recipe_list' %}active{% endif %}">
53
52
  <i>restaurant</i>
54
- <span>{% trans "Recipes" %}</span>
53
+ <span class="large-text">{% trans "Recipes" %}</span>
55
54
  </a>
56
- <a href="{% url 'admin_recipe_approval_list' %}" class="{% if request.resolver_match.url_name == 'admin_recipe_approval_list' %}active{% endif %}">
55
+ <a href="{% url 'admin_recipe_approval_list' %}" class="padding {% if request.resolver_match.url_name == 'admin_recipe_approval_list' %}active{% endif %}">
57
56
  <i>how_to_reg</i>
58
- <span>{% trans "Approvals" %}</span>
57
+ <span class="large-text">{% trans "Approvals" %}</span>
59
58
  </a>
60
- <a href="{% url 'admin_user_list' %}" class="{% if request.resolver_match.url_name == 'admin_user_list' %}active{% endif %}">
59
+ <a href="{% url 'admin_user_list' %}" class="padding {% if request.resolver_match.url_name == 'admin_user_list' %}active{% endif %}">
61
60
  <i>people</i>
62
- <span>{% trans "Users" %}</span>
61
+ <span class="large-text">{% trans "Users" %}</span>
63
62
  </a>
64
- <a href="{% url 'admin_tag_list' %}" class="{% if request.resolver_match.url_name == 'admin_tag_list' %}active{% endif %}">
63
+ <a href="{% url 'admin_tag_list' %}" class="padding {% if request.resolver_match.url_name == 'admin_tag_list' %}active{% endif %}">
65
64
  <i>label</i>
66
- <span>{% trans "Tags" %}</span>
65
+ <span class="large-text">{% trans "Tags" %}</span>
67
66
  </a>
68
- <a href="{% url 'admin_rating_list' %}" class="{% if request.resolver_match.url_name == 'admin_rating_list' %}active{% endif %}">
67
+ <a href="{% url 'admin_rating_list' %}" class="padding {% if request.resolver_match.url_name == 'admin_rating_list' %}active{% endif %}">
69
68
  <i>star</i>
70
- <span>{% trans "Ratings" %}</span>
69
+ <span class="large-text">{% trans "Ratings" %}</span>
71
70
  </a>
72
- <a href="{% url 'admin_order_list' %}" class="{% if request.resolver_match.url_name == 'admin_order_list' %}active{% endif %}">
71
+ <a href="{% url 'admin_order_list' %}" class="padding {% if request.resolver_match.url_name == 'admin_order_list' %}active{% endif %}">
73
72
  <i>shopping_cart</i>
74
- <span>{% trans "Orders" %}</span>
73
+ <span class="large-text">{% trans "Orders" %}</span>
75
74
  </a>
76
- <a href="{% url 'admin_task_list' %}" class="{% if request.resolver_match.url_name == 'admin_task_list' %}active{% endif %}">
75
+ <a href="{% url 'admin_settings' %}" class="padding {% if request.resolver_match.url_name == 'admin_settings' %}active{% endif %}">
76
+ <i>settings</i>
77
+ <span class="large-text">{% trans "Site Settings" %}</span>
78
+ </a>
79
+ <a href="{% url 'admin_task_list' %}" class="padding {% if request.resolver_match.url_name == 'admin_task_list' %}active{% endif %}">
77
80
  <i>assignment</i>
78
- <span>{% trans "Tasks" %}</span>
81
+ <span class="large-text">{% trans "Tasks" %}</span>
79
82
  </a>
80
83
  <div class="divider"></div>
81
- <a href="{% url 'index' %}">
84
+ <a href="{% url 'index' %}" class="padding">
82
85
  <i>home</i>
83
- <span>{% trans "Public Site" %}</span>
86
+ <span class="large-text">{% trans "Public Site" %}</span>
84
87
  </a>
85
88
  </nav>
86
89
  </dialog>
87
90
 
88
91
  {% if user.is_authenticated %}
89
- <menu id="user-menu" class="no-wrap left">
92
+ <menu id="user-menu" class="left">
90
93
  <a href="{% url 'admin:logout' %}" class="row"><i>logout</i>{% trans "Logout" %}</a>
91
94
  </menu>
92
95
  {% endif %}
@@ -13,11 +13,24 @@
13
13
  </div>
14
14
  </td>
15
15
  <td>{{ order.recipe.title }}</td>
16
- <td>{{ order.total_price }} €</td>
16
+ <td>€ {{ order.total_price }} </td>
17
17
  <td>
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>
18
+ <form method="post" action="{% url 'admin_order_update_status' order.pk %}" class="row align-center no-space">
19
+ {% csrf_token %}
20
+ <div class="field border round small no-margin">
21
+ <select name="status" onchange="this.form.submit()" {% if order.status == 'COMPLETED' or order.status == 'CANCELLED' %}disabled{% endif %}>
22
+ {% for val, label in status_choices %}
23
+ <option value="{{ val }}" {% if order.status == val %}selected{% endif %}>{{ label }}</option>
24
+ {% endfor %}
25
+ </select>
26
+ <i>arrow_drop_down</i>
27
+ </div>
28
+ {% if order.status == 'COMPLETED' or order.status == 'CANCELLED' %}
29
+ <span class="chip {% if order.status == 'COMPLETED' %}primary{% else %}error{% endif %} margin">
30
+ {{ order.get_status_display }}
31
+ </span>
32
+ {% endif %}
33
+ </form>
21
34
  </td>
22
35
  <td>{{ order.created_at|date:"d/m/Y H:i" }}</td>
23
36
  </tr>
@@ -38,7 +38,6 @@
38
38
  <th>{% trans "Rating" %}
39
39
  <i class="primary-text">star</i>
40
40
  </th>
41
- <th>{% trans "Approved" %}</th>
42
41
  <th>{% trans "Tags" %}</th>
43
42
  <th>
44
43
  <a href="?sort={% if current_sort == 'uploader' %}-uploader{% else %}uploader{% endif %}" class="row align-center">
@@ -75,7 +74,7 @@
75
74
  </td>
76
75
  <td>
77
76
  {% if recipe.price %}
78
- {{ recipe.price }}
77
+ {{ recipe.price }}
79
78
  {% else %}
80
79
  -
81
80
  {% endif %}
@@ -89,13 +88,6 @@
89
88
  <span>{{ recipe.avg_rating|default:0|floatformat:1 }}</span>
90
89
  </div>
91
90
  </td>
92
- <td class="center-align">
93
- {% if recipe.is_approved %}
94
- <i class="primary-text">check_circle</i>
95
- {% else %}
96
- <i class="primary-text">close</i>
97
- {% endif %}
98
- </td>
99
91
  <td>
100
92
  {% for tag in recipe.tags.all %}
101
93
  <span class="chip tiny">{{ tag.name }}</span>
@@ -0,0 +1,41 @@
1
+ {% extends "admin/admin_base.html" %}
2
+ {% load i18n %}
3
+
4
+ {% block admin_title %}{{ title }}{% endblock %}
5
+
6
+ {% block content %}
7
+ <main class="container">
8
+ <article class="round padding">
9
+ <header>
10
+ <h5 class="max">{% trans "Site Settings" %}</h5>
11
+ </header>
12
+ <div class="space"></div>
13
+ <form method="post">
14
+ {% csrf_token %}
15
+ <div class="grid">
16
+ {% for field in form %}
17
+ <div class="s12 m6">
18
+ <div class="field label border round">
19
+ {{ field }}
20
+ <label>{{ field.label }}</label>
21
+ {% if field.help_text %}
22
+ <div class="helper">{{ field.help_text }}</div>
23
+ {% endif %}
24
+ {% if field.errors %}
25
+ <span class="error">{{ field.errors|striptags }}</span>
26
+ {% endif %}
27
+ </div>
28
+ </div>
29
+ {% endfor %}
30
+ </div>
31
+ <div class="space"></div>
32
+ <nav class="right-align">
33
+ <button type="submit" class="button primary round">
34
+ <i>save</i>
35
+ <span>{% trans "Save Settings" %}</span>
36
+ </button>
37
+ </nav>
38
+ </form>
39
+ </article>
40
+ </main>
41
+ {% endblock %}
@@ -15,31 +15,31 @@
15
15
  {% if cart_items %}
16
16
  <div class="grid">
17
17
  <div class="s12 m8">
18
- <div class="padding border round surface">
19
- <table class="border striped no-space">
18
+ <div class="padding border round secondary-container">
19
+ <table class="border striped large-space">
20
20
  <thead>
21
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>
22
+ <th>{% trans "Item" %}</th>
23
+ <th>{% trans "Description" %}</th>
24
+ <th>{% trans "Quantity" %}</th>
25
+ <th>{% trans "Price" %}</th>
26
+ <th></th>
27
27
  </tr>
28
28
  </thead>
29
29
  <tbody>
30
30
  {% for item in cart_items %}
31
31
  <tr>
32
- <td class="min">
32
+ <td>
33
33
  {% if item.recipe.image %}
34
- <img src="{{ item.recipe.image_thumbnail.url }}" class="circle small">
34
+ <img src="{{ item.recipe.image_thumbnail.url }}" class="large circle" alt="{{ item.recipe.title }}">
35
35
  {% else %}
36
36
  <i class="circle small gray1">restaurant</i>
37
37
  {% endif %}
38
38
  </td>
39
- <td class="max">
39
+ <td>
40
40
  <a href="{% url 'recipe_detail' item.recipe.slug %}" class="bold">{{ item.recipe.title }}</a>
41
41
  </td>
42
- <td class="min">
42
+ <td>
43
43
  <form action="{% url 'update_cart_quantity' item.pk %}" method="post" class="row no-space align-center">
44
44
  {% csrf_token %}
45
45
  <div class="field border round small no-margin" style="width: 80px;">
@@ -47,8 +47,8 @@
47
47
  </div>
48
48
  </form>
49
49
  </td>
50
- <td class="min no-wrap">{{ item.total_price }} €</td>
51
- <td class="min">
50
+ <td>{{ item.total_price }} €</td>
51
+ <td>
52
52
  <a href="{% url 'remove_from_cart' item.pk %}" class="button circle transparent">
53
53
  <i>delete</i>
54
54
  </a>
@@ -66,7 +66,6 @@
66
66
  <div class="max">{% trans "Subtotal" %}</div>
67
67
  <div class="bold">{{ total }} €</div>
68
68
  </div>
69
- <div class="divider mt-1 mb-1"></div>
70
69
  <div class="row">
71
70
  <div class="max bold">{% trans "Total" %}</div>
72
71
  <div class="bold large-text">{{ total }} €</div>
@@ -74,14 +73,14 @@
74
73
  <div class="space"></div>
75
74
  <form action="{% url 'checkout_cart' %}" method="post">
76
75
  {% csrf_token %}
77
- <button type="submit" class="button primary round extend">
76
+ <button type="submit" class="button primary round">
78
77
  <i>check</i>
79
78
  <span>{% trans "Checkout" %}</span>
80
79
  </button>
81
80
  </form>
82
81
  </article>
83
82
  <div class="space"></div>
84
- <a href="{% url 'index' %}" class="button transparent extend">
83
+ <a href="{% url 'index' %}" class="button secondary round">
85
84
  <i>arrow_back</i>
86
85
  <span>{% trans "Continue Shopping" %}</span>
87
86
  </a>
@@ -6,6 +6,6 @@
6
6
  <input type="number" id="portions" value="{{ recipe.servings }}" min="1" onchange="scaleIngredients()">
7
7
  <button class="button circle transparent" onclick="adjustPortions(1)">+</button>
8
8
  </div>
9
- <div id="ingredients-display" class="card padding flat gray1">
9
+ <div id="ingredients-display" class="card padding">
10
10
  <p class="text-center">{% trans "Loading ingredients..." %}</p>
11
11
  </div>
@@ -12,7 +12,7 @@
12
12
  <a href="{% url 'view_cart' %}" class="button circle transparent">
13
13
  <i>shopping_cart</i>
14
14
  {% if user.cart_items.exists %}
15
- <badge class="red">{{ user.cart_items.count }}</badge>
15
+ <badge class="tertiary round small-padding">{{ user.cart_items.count }}</badge>
16
16
  {% endif %}
17
17
  </a>
18
18
  <a href="{% url 'user_profile' %}">
@@ -22,9 +22,9 @@
22
22
  <img src="https://www.w3schools.com/howto/img_avatar.png" class="circle">
23
23
  {% endif %}
24
24
  </a>
25
- <button class="circle transparent" data-ui="#user-menu">
25
+ <a href="{% url 'user_settings' %}" class="{% if request.resolver_match.url_name == 'user_settings' %}active{% endif %}">
26
26
  <i>more_vert</i>
27
- </button>
27
+ </a>
28
28
  {% else %}
29
29
  <a href="{% url 'login' %}"><button class="chip transparent border white-text">{% trans "Login" %}</button></a>
30
30
  <a href="{% url 'signup' %}"><button class="chip primary">{% trans "Sign up" %}</button></a>
@@ -40,6 +40,7 @@
40
40
  <textarea name="{{ rating_form.comment.name }}" placeholder="{% trans "Add a comment (optional)" %}">{{ user_rating.comment|default:"" }}</textarea>
41
41
  <label>{% trans "Comment" %}</label>
42
42
  </div>
43
+ <div class="space"></div>
43
44
  <button type="submit" class="button primary round mt-2">{% trans "Submit Rating" %}</button>
44
45
  </form>
45
46
  </article>
@@ -3,7 +3,7 @@
3
3
  <h4 class="bold max">{{ recipe.title }}</h4>
4
4
  {% if user.is_authenticated %}
5
5
  <a href="{% url 'toggle_favorite' recipe.pk %}" id="favorite-toggle-button" class="button circle transparent" title="{% trans 'Toggle Favorite' %}">
6
- <i class="{% if recipe in user.favorites.all %}primary-text{% else %}black-text{% endif %}">
6
+ <i class="{% if recipe in user.favorites.all %}red-text{% else %}primary-text{% endif %}">
7
7
  {% if recipe in user.favorites.all %}favorite{% else %}favorite_border{% endif %}
8
8
  </i>
9
9
  </a>
@@ -19,10 +19,6 @@
19
19
  <i class="extra padding">group</i>
20
20
  <span class="large-text">{% trans "Community" %}</span>
21
21
  </a>
22
- <a href="{% url 'user_settings' %}" class="padding {% if request.resolver_match.url_name == 'user_settings' %}active{% endif %}">
23
- <i class="extra padding">settings</i>
24
- <span class="large-text">{% trans "Settings" %}</span>
25
- </a>
26
22
  {% endif %}
27
23
  <a href="/api/docs" class="padding">
28
24
  <i class="extra padding">api</i>
@@ -81,10 +81,9 @@
81
81
 
82
82
  <div class="large-space"></div>
83
83
 
84
- <div class="grid">
85
- <!-- Left Column: Image and Main Info -->
86
- <div class="s12 m12 l5">
87
- <article class="round no-padding elevate">
84
+ <div>
85
+ <aside class="left rounded padding">
86
+ <article class="round no-padding elevate">
88
87
  {% if recipe.image %}
89
88
  <img src="{{ recipe.image_large.url }}"
90
89
  srcset="{{ recipe.image_medium.url }} 700w, {{ recipe.image_large.url }} 1200w"
@@ -127,8 +126,6 @@
127
126
  {% endif %}
128
127
  </div>
129
128
 
130
- <div class="space"></div>
131
- <div class="divider"></div>
132
129
  <div class="space"></div>
133
130
 
134
131
  {% include "components/rating_section.html" %}
@@ -142,31 +139,26 @@
142
139
  {% endif %}
143
140
  </div>
144
141
  </article>
145
- </div>
146
-
147
- <!-- Right Column: Details -->
148
- <div class="s12 m12 l7">
149
- <div class="padding">
150
-
151
- <h5 class="primary-text">{% trans "Description" %}</h5>
152
- <div class="large-text">
153
- {% if recipe.description %}
154
- {{ recipe.description|convert_markdown|safe }}
155
- {% else %}
156
- <p class="italic">{% trans "No description yet." %}</p>
157
- {% endif %}
158
- </div>
159
-
160
- <div class="large-space"></div>
161
-
162
- {% include "components/ingredients_section.html" %}
163
-
164
- <div class="large-space"></div>
165
-
166
- {% include "components/instructions_section.html" %}
167
- </div>
168
- </div>
142
+ </aside>
143
+ <h5>{{ recipe.title }}</h5>
144
+ <div class="large-padding">
145
+ <div class="page active">
146
+ <h5 class="primary-text">{% trans "Description" %}</h5>
147
+ <div class="large-text">
148
+ {% if recipe.description %}
149
+ {{ recipe.description|convert_markdown|safe }}
150
+ {% else %}
151
+ <p class="italic">{% trans "No description yet." %}</p>
152
+ {% endif %}
153
+ </div>
154
+ <div class="small-space"></div>
155
+ {% include "components/ingredients_section.html" %}
156
+ <div class="small-space"></div>
157
+ {% include "components/instructions_section.html" %}
158
+ </div>
159
+ </div>
169
160
  </div>
161
+
170
162
  {% endblock %}
171
163
 
172
164
  {% block extra_scripts %}
@@ -36,10 +36,10 @@
36
36
  <h4 class="upper center-align">{% trans 'Highlights' %}</h4>
37
37
  <div class="small-space"></div>
38
38
  <div class="relative">
39
- <div class="row scroll snap no-scrollbar" id="highlighted-carousel" style="gap: 20px; flex-wrap: nowrap;">
39
+ <div class="row scroll snap no-scrollbar large-padding" id="highlighted-carousel" style="gap: 20px; flex-wrap: nowrap;">
40
40
  {% for recipe in highlighted_recipes %}
41
41
  <a href="{{ recipe.get_absolute_url }}">
42
- <article class="round no-padding elevate" style="min-width: 300px; width: 300px; flex-shrink: 0; scroll-snap-align: start;">
42
+ <article class="round no-padding elevate tertiary-container" style="min-width: 300px; width: 300px; flex-shrink: 0; scroll-snap-align: start;">
43
43
  {% if recipe.image %}
44
44
  <img src="{{ recipe.image_medium.url }}" class="responsive top-round" alt="{{ recipe.title }}" style="height: 200px; object-fit: cover;">
45
45
  {% else %}
@@ -54,7 +54,6 @@
54
54
  </a>
55
55
  {% endfor %}
56
56
  </div>
57
-
58
57
  <button class="circle surface absolute left middle-align elevate" onclick="scrollCarousel(-1)" style="top: 50%; transform: translateY(-50%); left: -20px; z-index: 10;">
59
58
  <i>chevron_left</i>
60
59
  </button>
@@ -2,7 +2,7 @@
2
2
  <div class="grid justify-center" id="recipe-grid">
3
3
  {% for recipe in recipes %}
4
4
  <div class="s12 m6 l4 xl3 recipe-card">
5
- <article class="round no-padding elevate">
5
+ <article class="round no-padding elevate secondary-container">
6
6
  <div style="position: relative;">
7
7
  {% if recipe.image %}
8
8
  <img src="{{ recipe.image_medium.url }}" class="responsive top-round" style="height:220px; width:100%; object-fit:cover; cursor:pointer;" loading="lazy" alt="{{ recipe.title }}" onclick="location.href='{% url 'recipe_detail' recipe.slug %}';">
@@ -14,18 +14,27 @@
14
14
 
15
15
  {% if user.is_authenticated %}
16
16
  <div class="absolute top left padding">
17
- <a href="{% url 'toggle_favorite' recipe.pk %}" class="button circle surface white-text">
18
- <i class="{% if recipe in user.favorites.all %}primary-text{% else %}black-text{% endif %}">
17
+ <a href="{% url 'toggle_favorite' recipe.pk %}" class="button circle transparent">
18
+ <i class="{% if recipe in user.favorites.all %}red-text{% else %}black-text{% endif %}">
19
19
  {% if recipe in user.favorites.all %}favorite{% else %}favorite_border{% endif %}
20
20
  </i>
21
21
  </a>
22
22
  </div>
23
23
  {% endif %}
24
+ {% if user.is_authenticated and recipe.price %}
25
+ <div class="absolute top right padding">
26
+ <form action="{% url 'add_to_cart' recipe.pk %}" method="post" class="ml-1">
27
+ {% csrf_token %}
28
+ <button type="submit" class="button circle primary" title="{% trans 'Add to Cart' %}">
29
+ <i>add_shopping_cart</i>
30
+ </button>
31
+ </form>
32
+ </div>
33
+ {% endif %}
24
34
  </div>
25
35
 
26
- <div class="padding">
36
+ <div class="padding grow">
27
37
  <h5 class="truncate pointer" onclick="location.href='{% url 'recipe_detail' recipe.slug %}';">{{ recipe.title }}</h5>
28
-
29
38
  {% if recipe.price %}
30
39
  <div class="row align-center mb-1">
31
40
  <i class="small-text primary-text">euro_symbol</i>
@@ -48,23 +57,27 @@
48
57
  <span class="tiny-text left-margin">{% trans "Unknown" %}</span>
49
58
  {% endif %}
50
59
  </div>
51
-
52
60
  <div class="space"></div>
53
-
54
61
  <div class="row wrap">
55
- {% if recipe.favorited_by.all %}
62
+ {% with favoriters=recipe.favorited_by.all %}
63
+ {% with count=favoriters.count %}
64
+ {% if count > 0 %}
56
65
  <span class="tiny-text">
57
- {% if recipe.favorited_by.count == 1 %}
58
- {% trans "Liked by" %} {{ recipe.favorited_by.first.username }}
66
+ {% trans "Liked by" %}
67
+ {% if count == 1 %}
68
+ {{ favoriters.0.username }}
69
+ {% elif count == 2 %}
70
+ {{ favoriters.0.username }} {% trans "and" %} {{ favoriters.1.username }}
59
71
  {% else %}
60
- {% trans "Liked by" %} {{ recipe.favorited_by.first.username }} {% trans "and" %} {{ recipe.favorited_by.count|add:"-1" }} {% trans "others" %}
72
+ {{ favoriters.0.username }}, {{ favoriters.1.username }} {% trans "and" %} {{ count|add:"-2" }} {% trans "others" %}
61
73
  {% endif %}
62
74
  </span>
63
75
  {% endif %}
76
+ {% endwith %}
77
+ {% endwith %}
64
78
  </div>
65
79
 
66
80
  <div class="space"></div>
67
-
68
81
  <div class="row wrap">
69
82
  {% for tag in recipe.tags.all %}
70
83
  <a href="{% url 'index' %}?tag={{ tag.name|urlencode }}" class="chip small round surface margin-bottom">{{ tag.name }}</a>
@@ -77,14 +90,6 @@
77
90
  <span>{% trans 'View' %}</span>
78
91
  <i class="suffix">arrow_forward</i>
79
92
  </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 %}
88
93
  </div>
89
94
  </article>
90
95
  </div>
@@ -5,8 +5,7 @@
5
5
  {% block content %}
6
6
  <div class="large-space"></div>
7
7
 
8
- <div class="grid">
9
- <div class="s12 m10 l8 xl6 middle-align center-align" style="margin: 0 auto;">
8
+ <div class="s12 m10 l8 xl6 middle-align center-align" >
10
9
  <article class="round elevate">
11
10
  <div class="padding">
12
11
  <h4 class="center-align primary-text">{% trans "Edit your profile" %}</h4>
@@ -73,7 +72,7 @@
73
72
  </label>
74
73
  </div>
75
74
  {% if user.avatar %}
76
- <img src="{{ user.avatar.url }}" class="circle small">
75
+ <img src="{{ user.avatar.url }}" class="circle">
77
76
  {% endif %}
78
77
  </nav>
79
78
  {% if form.avatar.errors %}
@@ -91,13 +90,13 @@
91
90
 
92
91
  </form>
93
92
  </article>
93
+ </div>
94
94
 
95
95
  <div class="large-space"></div>
96
-
97
- <h4 class="center-align primary-text">{% trans "Order History" %}</h4>
98
-
96
+ <div class="middle-align center-align ">
97
+ <h4 class="center-align padding">{% trans "Order History" %}</h4>
99
98
  <form method="get" class="row no-wrap middle-align">
100
- <div class="field label border round small">
99
+ <div class="field label border round">
101
100
  <select name="status" onchange="this.form.submit()">
102
101
  <option value="">{% trans "All Statuses" %}</option>
103
102
  {% for code, label in status_choices %}
@@ -107,7 +106,7 @@
107
106
  <label>{% trans "Filter by Status" %}</label>
108
107
  </div>
109
108
  <div class="space"></div>
110
- <div class="field label border round small">
109
+ <div class="field label border round">
111
110
  <select name="sort" onchange="this.form.submit()">
112
111
  <option value="date_desc" {% if current_sort == 'date_desc' %}selected{% endif %}>{% trans "Newest First" %}</option>
113
112
  <option value="date_asc" {% if current_sort == 'date_asc' %}selected{% endif %}>{% trans "Oldest First" %}</option>
@@ -117,18 +116,19 @@
117
116
  <label>{% trans "Sort by" %}</label>
118
117
  </div>
119
118
  </form>
119
+ </div>
120
120
 
121
121
  {% if orders %}
122
122
  <div class="padding border round surface left-align">
123
- <table class="border striped">
123
+ <table class="border striped large-space">
124
124
  <thead>
125
125
  <tr>
126
- <th class="min">#</th>
127
- <th class="max">{% trans "Recipe" %}</th>
128
- <th class="min">{% trans "Date" %}</th>
129
- <th class="min">{% trans "Status" %}</th>
130
- <th class="min">{% trans "Price" %}</th>
131
- <th class="min"></th>
126
+ <th>{% trans "Order Id" %}</th>
127
+ <th>{% trans "Recipe" %}</th>
128
+ <th>{% trans "Date" %}</th>
129
+ <th>{% trans "Status" %}</th>
130
+ <th>{% trans "Price" %}</th>
131
+ <th>{% trans "View Order" %}</th>
132
132
  </tr>
133
133
  </thead>
134
134
  <tbody>
@@ -138,13 +138,13 @@
138
138
  <td>
139
139
  <a href="{% url 'recipe_detail' order.recipe.slug %}">{{ order.recipe.title }}</a>
140
140
  </td>
141
- <td class="no-wrap">{{ order.created_at|date:"d/m/Y" }}</td>
141
+ <td>{{ order.created_at|date:"d/m/Y" }}</td>
142
142
  <td>
143
143
  <span class="chip tiny {% if order.status == 'PENDING' %}surface-variant{% elif order.status == 'COMPLETED' %}primary{% elif order.status == 'CANCELLED' %}error{% else %}secondary{% endif %}">
144
144
  {{ order.get_status_display }}
145
145
  </span>
146
146
  </td>
147
- <td class="no-wrap">{{ order.total_price }} €</td>
147
+ <td>{{ order.total_price }} €</td>
148
148
  <td>
149
149
  <a href="{% url 'user_order_detail' order.id %}" class="button circle transparent small">
150
150
  <i>visibility</i>
@@ -182,5 +182,5 @@
182
182
  </div>
183
183
  {% endif %}
184
184
  </div>
185
- </div>
185
+
186
186
  {% endblock %}
sandwitches/urls.py CHANGED
@@ -120,7 +120,13 @@ urlpatterns += i18n_patterns(
120
120
  views.admin_rating_delete,
121
121
  name="admin_rating_delete",
122
122
  ),
123
+ path("dashboard/settings/", views.admin_settings, name="admin_settings"),
123
124
  path("dashboard/orders/", views.admin_order_list, name="admin_order_list"),
125
+ path(
126
+ "dashboard/orders/<int:pk>/status/",
127
+ views.admin_order_update_status,
128
+ name="admin_order_update_status",
129
+ ),
124
130
  prefix_default_language=True,
125
131
  )
126
132
 
sandwitches/utils.py CHANGED
@@ -51,6 +51,9 @@ COMMON_UNITS = {
51
51
  "to taste",
52
52
  }
53
53
 
54
+ # Simple in-memory DB for order status tracking
55
+ ORDER_DB = {}
56
+
54
57
 
55
58
  def parse_ingredient_line(line):
56
59
  """
sandwitches/views.py CHANGED
@@ -9,7 +9,8 @@ from django.contrib.auth.decorators import login_required
9
9
  from django.contrib.admin.views.decorators import staff_member_required
10
10
  from django.utils.translation import gettext as _
11
11
  from django.utils import translation
12
- from .models import Recipe, Rating, Tag, Order, CartItem
12
+ from .models import Recipe, Rating, Tag, Order, CartItem, Setting
13
+ from .utils import ORDER_DB
13
14
  from .forms import (
14
15
  RecipeForm,
15
16
  AdminSetupForm,
@@ -20,6 +21,7 @@ from .forms import (
20
21
  UserProfileForm,
21
22
  UserRecipeSubmissionForm,
22
23
  UserSettingsForm,
24
+ SettingForm,
23
25
  )
24
26
  from django.http import HttpResponseBadRequest, Http404
25
27
  from django.conf import settings
@@ -491,6 +493,52 @@ def admin_rating_delete(request, pk):
491
493
  )
492
494
 
493
495
 
496
+ @staff_member_required
497
+ def admin_settings(request):
498
+ instance = Setting.get_solo()
499
+ if request.method == "POST":
500
+ form = SettingForm(request.POST, request.FILES, instance=instance)
501
+ if form.is_valid():
502
+ form.save()
503
+ messages.success(request, _("Site settings updated successfully."))
504
+ return redirect("admin_settings")
505
+ else:
506
+ form = SettingForm(instance=instance)
507
+ return render(
508
+ request,
509
+ "admin/settings.html",
510
+ {"form": form, "title": _("Site Settings"), "version": sandwitches_version},
511
+ )
512
+
513
+
514
+ @staff_member_required
515
+ def admin_order_update_status(request, pk):
516
+ order = get_object_or_404(Order, pk=pk)
517
+ if request.method == "POST":
518
+ new_status = request.POST.get("status")
519
+ if new_status in dict(Order.STATUS_CHOICES):
520
+ # Immutability check: if status is CANCELLED or COMPLETED, it shouldn't be changed
521
+ if order.status in ["COMPLETED", "CANCELLED"]:
522
+ messages.error(
523
+ request,
524
+ _("Cannot change status of a completed or cancelled order."),
525
+ )
526
+ else:
527
+ order.status = new_status
528
+ if new_status == "COMPLETED":
529
+ order.completed = True
530
+ order.save()
531
+
532
+ # Update ORDER_DB
533
+ ORDER_DB[order.pk] = new_status
534
+
535
+ messages.success(request, _("Order status updated."))
536
+ else:
537
+ messages.error(request, _("Invalid status."))
538
+
539
+ return redirect("admin_order_list")
540
+
541
+
494
542
  @staff_member_required
495
543
  def admin_order_list(request):
496
544
  orders = (
@@ -503,13 +551,21 @@ def admin_order_list(request):
503
551
  return render(
504
552
  request,
505
553
  "admin/partials/order_rows.html",
506
- {"orders": orders, "version": sandwitches_version},
554
+ {
555
+ "orders": orders,
556
+ "version": sandwitches_version,
557
+ "status_choices": Order.STATUS_CHOICES,
558
+ },
507
559
  )
508
560
 
509
561
  return render(
510
562
  request,
511
563
  "admin/order_list.html",
512
- {"orders": orders, "version": sandwitches_version},
564
+ {
565
+ "orders": orders,
566
+ "version": sandwitches_version,
567
+ "status_choices": Order.STATUS_CHOICES,
568
+ },
513
569
  )
514
570
 
515
571
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sandwitches
3
- Version: 2.5.2
3
+ Version: 2.5.3
4
4
  Summary: Add your description here
5
5
  Author: Martyn van Dijke
6
6
  Author-email: Martyn van Dijke <martijnvdijke600@gmail.com>
@@ -31,16 +31,17 @@ sandwitches/models.py,sha256=L8qMFoJ7WpIito2nTgsAB8s-UPUQMwDux63aKbP71aE,12468
31
31
  sandwitches/settings.py,sha256=5_eQAJCAV093hnhr3XOxHekT4IF-PEJcRiTecq71_SQ,5841
32
32
  sandwitches/storage.py,sha256=ibBG6tVtArqzgEKsRimZPwsqW7i9j4WiPLLHrOJchow,3578
33
33
  sandwitches/tasks.py,sha256=cGPgEqxGOCwliIq0oxNZBGjnCtvrTdJN-XyIp1ciJ3k,6865
34
- sandwitches/templates/admin/admin_base.html,sha256=MGd_i3YCgczGkTFUof2XlxUnAQJ6XP7YMcghdIeG2zQ,4135
34
+ sandwitches/templates/admin/admin_base.html,sha256=a3eA0zTEDFhnX0ZFfMlPanmUImMTHmrqOO_2co2MUdE,4528
35
35
  sandwitches/templates/admin/confirm_delete.html,sha256=HfsZI_gV8JQTKz215TYgPWBrgrFhGv1UB3N-0Hln-14,804
36
36
  sandwitches/templates/admin/dashboard.html,sha256=ExLPzEmg7h0SvCuXr0uG1INDPLyirIBfD3Yvw4A1zjQ,5355
37
37
  sandwitches/templates/admin/order_list.html,sha256=eHFUn2speXaaj5_SFUG0Z0HfWVUR9-VCDRBeb8ufFb0,819
38
38
  sandwitches/templates/admin/partials/dashboard_charts.html,sha256=NYrt-LDZO4__2KDWhAYL5K_f-2Zgj0iiuaZQiRZlBWg,3639
39
- sandwitches/templates/admin/partials/order_rows.html,sha256=C7_ArHw1udaGjx6CRJHhksje0OReP7UUhdsHcdFPqCc,940
39
+ sandwitches/templates/admin/partials/order_rows.html,sha256=4ogAFno4rfQJTyT4QQpKIaGqtI8akIo37xyZPsR7iCc,1638
40
40
  sandwitches/templates/admin/rating_list.html,sha256=8CHAsBfKfs4izhb-IyOiDjJXqAZxFcStoRSGh4pRlgM,1365
41
41
  sandwitches/templates/admin/recipe_approval_list.html,sha256=M6GFYI45lAkLkvqP44cu5tDYVOeeVNklEphof1euesM,2281
42
42
  sandwitches/templates/admin/recipe_form.html,sha256=wVKKBFl3vN11aknnmv2Hxkj66zZk9iZ0x_iS1j_X_Ro,12884
43
- sandwitches/templates/admin/recipe_list.html,sha256=P2lNACOEzwOeXk6uoEtXhvUlHADV--OkFjvSMapnC40,4985
43
+ sandwitches/templates/admin/recipe_list.html,sha256=Rn5AjHPdaSDGt1MwxVeAobgdRmg3-uam6XM-92S5nT0,4675
44
+ sandwitches/templates/admin/settings.html,sha256=UeuLVIKg9AMWPTW8kbRLBH8Zkt6wRVmA9kiBt4ZHem0,1387
44
45
  sandwitches/templates/admin/tag_form.html,sha256=JRWgAl4fz_Oy-Kuo1K6Mex_CXdsHMABzzyPazthr1Kg,989
45
46
  sandwitches/templates/admin/tag_list.html,sha256=OCtoOg11O5BxSg1hoBDUp_nBJhtQ-XUZewTJjDl3L-4,1028
46
47
  sandwitches/templates/admin/task_detail.html,sha256=dO5zHOG-yTY1ly3VPA3o3jjie0wmxw0gdddtSKpN-W8,3825
@@ -49,38 +50,38 @@ sandwitches/templates/admin/user_form.html,sha256=7_6GShLROFeJJexL1XLFUXdW9_lYF8
49
50
  sandwitches/templates/admin/user_list.html,sha256=Mnxu9mwR-j6VchcU3ahbDuKhajX3gPoQaw_GirT0oW4,2056
50
51
  sandwitches/templates/base.html,sha256=4IkEdeEksQ9o4Ad72osMgMVnzz14aJa8WKg9HE3F0MU,3655
51
52
  sandwitches/templates/base_beer.html,sha256=7dWwGOUEdBz19X3Rilbam1xSNkQqXqvC6cYrDdQvYIE,2025
52
- sandwitches/templates/cart.html,sha256=YqmrzOLLPAXSqeXeUTrt9AwTTWOitOLTaD_k3mYYVpM,4537
53
+ sandwitches/templates/cart.html,sha256=nQ22sdwT5CDdhgtizPWUhye6IVUbN8jkA6zmob3p2M8,4390
53
54
  sandwitches/templates/community.html,sha256=-YhpPtLbrVK9mc2Go1XBInLK-7OXrtb7kKukjl7rGbg,9607
54
55
  sandwitches/templates/components/carousel_scripts.html,sha256=9vEL5JJv8zUUjEtsnHW-BwwXUNWqQ6w_vf6UdxgEv_I,1934
55
56
  sandwitches/templates/components/favorites_search_form.html,sha256=tpD8SpS47TUDJBwxhMuvjhTN9pjWoRGFW50TBv48Ld4,5202
56
57
  sandwitches/templates/components/footer.html,sha256=Qk-myRtXS6-1b3fMowVGnSuFb_UkUgX6BYX9zgh_SV8,486
57
58
  sandwitches/templates/components/ingredients_scripts.html,sha256=2zKTC65GYF589uW1sCpDq0jOkS3BnsuOwUpJbbigrn4,1794
58
- sandwitches/templates/components/ingredients_section.html,sha256=XsaVXTs9MIwjfJeLjlzah3GWWj8oFU-_HJd9i9l1HAo,665
59
+ sandwitches/templates/components/ingredients_section.html,sha256=bq3sbR1arTmK9n3r6vsxJWejLgglEtFa6ZBh3Bppq_I,654
59
60
  sandwitches/templates/components/instructions_section.html,sha256=RFlA4uPiI6vf1e2QgiD5KzGoy7Vg7y7nFY7TFabCYLA,277
60
- sandwitches/templates/components/navbar.html,sha256=UO0F4J_yDTOGo-C7thNNZu_9IJ0t1pDyu5mwm9L-Ql8,1166
61
- sandwitches/templates/components/rating_section.html,sha256=8O5IsFfQwnElMQZLnDpJiuCvvQMLa3jCS67u_RhMi7o,2717
62
- sandwitches/templates/components/recipe_header.html,sha256=U6CxuR275QD9TIqo3VQftqvV6tqmH1rJdDcQotnXxYM,2001
61
+ sandwitches/templates/components/navbar.html,sha256=pU_8I0GP6jgdyYg-6VHuY6YBYgdFjESnYljzY-hvYF0,1251
62
+ sandwitches/templates/components/rating_section.html,sha256=rAfVcIrdcsnhTfoGK5j_s2C8V36Bkqcm3EP8fAxWzPI,2753
63
+ sandwitches/templates/components/recipe_header.html,sha256=SMVd93FkDQU29Pi3l29kau9WN6zv--Z1YibUAGrH8dI,1999
63
64
  sandwitches/templates/components/search_form.html,sha256=B8579Jo44gLrlmvkkc2-Vuv_QD93Ljt6F2J1WgTDV60,6617
64
65
  sandwitches/templates/components/search_scripts.html,sha256=HvsO5e50DoTZeoFiYeNP5S8S5h7Zfr9VULOWKKR1i_M,3423
65
- sandwitches/templates/components/side_menu.html,sha256=FMy-gwjBh4JjMD8XcQ1nHBzf6_YoxY97flrafIN30RY,2055
66
+ sandwitches/templates/components/side_menu.html,sha256=qXYyk8W-GnG-u_mJ65ADa4HWfUa2ubxnQAKwxwacF9M,1787
66
67
  sandwitches/templates/components/user_menu.html,sha256=KV_0gtvWbo3nvKZQ0peSZDtB3GgVuOyeBdar-wmwsDg,461
67
- sandwitches/templates/detail.html,sha256=g-O_RsW9Ix9ivWC0nZ4FwHY2NhgYZ3bEGLpqGY0JSxg,5642
68
+ sandwitches/templates/detail.html,sha256=_0Cc7Tba8GCQMJ5UO_ltf_gkvsDeEM5EtRRa3yQSm_Y,5501
68
69
  sandwitches/templates/favorites.html,sha256=0cPpW07N6Isrb8XpvA5Eh97L2-12QFZ43EzeJvbOlXo,917
69
- sandwitches/templates/index.html,sha256=7anU7k8s80JYk59Rwsm8EdlNYd7B5clCvV7pKq2IUy0,2518
70
+ sandwitches/templates/index.html,sha256=XYECUStc0zX4XG_OBLWM4FUdUkMML4ZKV9tB2Cu71Fg,2546
70
71
  sandwitches/templates/login.html,sha256=LiQskhkOkfx0EE4ssA1ToqQ3oEll08OPYLDIkLjHfU8,2177
71
72
  sandwitches/templates/order_detail.html,sha256=D6MjUVibQuED2VRNHSjKVnLHcLgFtLvcVmuwlzfoJzo,2498
72
- sandwitches/templates/partials/recipe_list.html,sha256=LUHKFKG90D72K9X2X3d1osvj2jX1QU_MbPe0lNwRSII,4555
73
- sandwitches/templates/profile.html,sha256=9Sj0UjbUNmWHEG9Nnc95LK5P3Gl86FqsnikDsubJERY,8670
73
+ sandwitches/templates/partials/recipe_list.html,sha256=lED9_V4UKXqzsnbNSMrIem3iEXA4wiETtqzMlZPObxQ,4886
74
+ sandwitches/templates/profile.html,sha256=TMh07Bxs32svn4aV01hjf22sJYB54qnPw6tJ3deXtbw,8617
74
75
  sandwitches/templates/settings.html,sha256=Q3dwXdwiNgPxevfArjiHhIu-wCsQaii2Uymf_CqbWWg,2121
75
76
  sandwitches/templates/setup.html,sha256=iNveFgePATsCSO4XMbGPa8TnWHyvj8S_5WwcW6i7pbo,4661
76
77
  sandwitches/templates/signup.html,sha256=wVRyj6Sy1z5TRvJDT9ufEOp8-tKbW44Fzer4SYq9AGw,5518
77
78
  sandwitches/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
79
  sandwitches/templatetags/custom_filters.py,sha256=0KDFlFz4b5LwlcURBAmzyYWKKea-LwydZytJGVkkuKA,243
79
80
  sandwitches/templatetags/markdown_extras.py,sha256=0ibmRzxE3r85x4k7kK71R-9UT0CgeegYF7MHzj3juTI,344
80
- sandwitches/urls.py,sha256=0p7MpYranWc4qNPyInxNzg8J3vNhap_gdt2n892exJM,5006
81
- sandwitches/utils.py,sha256=SJP-TkeRZ0OIfaMigYrOSbxRqYXswoqoWhwll3nFuAM,7245
82
- sandwitches/views.py,sha256=usKEOSnYUF-aQ5LYsXFa_J2APYgHO_h0jvu7cUorOt0,32965
81
+ sandwitches/urls.py,sha256=CPLC8WyC2H2KvJEq74J2w7PHK6l4nAySWlAmn_hnlQA,5229
82
+ sandwitches/utils.py,sha256=Vxi89fIHge897P_zDDcE5aXHviYLgePXkinj2_lCCPM,7308
83
+ sandwitches/views.py,sha256=xxCYx5FM8oxk5GGstUQ2fI5rk6j-4xZ51BmilKzUvVo,34804
83
84
  sandwitches/wsgi.py,sha256=Eyncpnahq_4s3Lr9ruB-R3Lu9j9zBXqgPbUj7qhIbwU,399
84
- sandwitches-2.5.2.dist-info/WHEEL,sha256=e_m4S054HL0hyR3CpOk-b7Q7fDX6BuFkgL5OjAExXas,80
85
- sandwitches-2.5.2.dist-info/METADATA,sha256=__auuJ3Gqh58507_QvFufqdeAv-B24p_cAMkPmF94MA,3143
86
- sandwitches-2.5.2.dist-info/RECORD,,
85
+ sandwitches-2.5.3.dist-info/WHEEL,sha256=fAguSjoiATBe7TNBkJwOjyL1Tt4wwiaQGtNtjRPNMQA,80
86
+ sandwitches-2.5.3.dist-info/METADATA,sha256=kx_4HUOQDkWTkSmKKaJ9C7Oh9eJLfN8xwBxO0HB-h94,3143
87
+ sandwitches-2.5.3.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.27
2
+ Generator: uv 0.9.28
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any