sandwitches 2.1.0__py3-none-any.whl → 2.1.2__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/forms.py CHANGED
@@ -149,8 +149,8 @@ class RecipeForm(forms.ModelForm):
149
149
  # PIL rotates counter-clockwise by default, our 'rotation' is clockwise
150
150
  img = img.rotate(-rotation, expand=True)
151
151
  img.save(recipe.image.path)
152
- except Exception:
153
- pass
152
+ except Exception as e:
153
+ print(f"Error rotating image: {e}")
154
154
 
155
155
  if commit:
156
156
  recipe.set_tags_from_string(self.cleaned_data.get("tags_string", ""))
sandwitches/tasks.py CHANGED
@@ -51,7 +51,7 @@ def send_emails(recipe_id, emails):
51
51
 
52
52
  raw_message_fmt = _("""
53
53
  Hungry? We just added <strong>%(title)s</strong> to our collection.
54
-
54
+
55
55
  It's a delicious recipe that you won't want to miss!
56
56
  %(description)s
57
57
 
@@ -33,7 +33,7 @@
33
33
  <button class="circle transparent" onclick="toggleMode()">
34
34
  <i>dark_mode</i>
35
35
  </button>
36
-
36
+
37
37
  {% if user.avatar %}
38
38
  <img src="{{ user.avatar.url }}" class="circle" data-ui="#user-menu">
39
39
  {% else %}
@@ -9,9 +9,9 @@
9
9
  <h5>{% trans "Are you sure?" %}</h5>
10
10
  <p>{% trans "You are about to delete the following" %} {{ type }}: <b>{{ object }}</b></p>
11
11
  <p>{% trans "This action cannot be undone." %}</p>
12
-
12
+
13
13
  <div class="space"></div>
14
-
14
+
15
15
  <form method="post">
16
16
  {% csrf_token %}
17
17
  <nav class="center-align">
@@ -259,4 +259,4 @@
259
259
  });
260
260
  });
261
261
  </script>
262
- {% endblock %}
262
+ {% endblock %}
@@ -47,7 +47,7 @@
47
47
  <label>{{ form.title.label }}</label>
48
48
  {% if form.title.errors %}<span class="error">{{ form.title.errors|striptags }}</span>{% endif %}
49
49
  </div>
50
-
50
+
51
51
  <div class="field label border round mt-1">
52
52
  {{ form.tags_string }}
53
53
  <label>{{ form.tags_string.label }}</label>
@@ -74,14 +74,14 @@
74
74
  {% if form.description.errors %}<span class="error">{{ form.description.errors|striptags }}</span>{% endif %}
75
75
  </div>
76
76
  </div>
77
-
77
+
78
78
  <div class="s12 m4">
79
79
  <article class="round padding border center-align">
80
80
  <h6 class="bold mb-1">{% trans "Recipe Image" %}</h6>
81
-
81
+
82
82
  <div class="relative mb-1" style="overflow: hidden; min-height: 200px; display: flex; align-items: center; justify-content: center;">
83
83
  {% if recipe.image %}
84
- <img src="{{ recipe.image_medium.url }}" class="responsive round" id="image-preview" style="max-height: 300px; width: 100%; object-fit: contain; transition: transform 0.3s ease;">
84
+ <img src="{{ recipe.image_medium.url }}?v={% now "U" %}" class="responsive round" id="image-preview" style="max-height: 300px; width: 100%; object-fit: contain; transition: transform 0.3s ease;">
85
85
  {% else %}
86
86
  <div class="medium-height middle-align center-align gray1 round" id="image-placeholder" style="width: 100%;">
87
87
  <i class="extra">image</i>
@@ -100,7 +100,7 @@
100
100
  </button>
101
101
  </div>
102
102
  {% endif %}
103
-
103
+
104
104
  <div class="field file border round">
105
105
  <input type="text" readonly>
106
106
  {{ form.image }}
@@ -114,7 +114,7 @@
114
114
  <!-- Ingredients and Instructions (Full Width) -->
115
115
  <div class="s12">
116
116
  <div class="divider mb-2"></div>
117
-
117
+
118
118
  <div class="form-section">
119
119
  <div class="row align-center mb-1">
120
120
  <i class="primary-text">shopping_cart</i>
@@ -153,7 +153,7 @@
153
153
  {% block admin_scripts %}
154
154
  <script>
155
155
  let currentRotation = 0;
156
-
156
+
157
157
  function rotatePreview(angle) {
158
158
  currentRotation = (currentRotation + angle) % 360;
159
159
  const img = document.getElementById('image-preview');
@@ -168,7 +168,7 @@
168
168
  fields.forEach(id => {
169
169
  const el = document.getElementById(id);
170
170
  if (el) {
171
- new EasyMDE({
171
+ new EasyMDE({
172
172
  element: el,
173
173
  spellChecker: false,
174
174
  autosave: {
@@ -14,7 +14,7 @@
14
14
  <article class="round padding border">
15
15
  <h6 class="bold mb-1">{% trans "General Information" %}</h6>
16
16
  <div class="divider mb-1"></div>
17
-
17
+
18
18
  <div class="row mb-1">
19
19
  <div class="max bold">{% trans "Task Path" %}:</div>
20
20
  <div class="max"><code>{{ task.task_path }}</code></div>
@@ -39,9 +39,9 @@
39
39
  <div class="max bold">{% trans "Priority" %}:</div>
40
40
  <div class="max">{{ task.priority }}</div>
41
41
  </div>
42
-
42
+
43
43
  <div class="space"></div>
44
-
44
+
45
45
  <h6 class="bold mb-1">{% trans "Timeline" %}</h6>
46
46
  <div class="divider mb-1"></div>
47
47
  <div class="row mb-1">
@@ -85,10 +85,10 @@
85
85
  document.querySelectorAll('form').forEach(form => {
86
86
  form.addEventListener('submit', showLoading);
87
87
  });
88
-
88
+
89
89
  // Hide loading animation on page load completion (after initial DOMContentLoaded)
90
90
  window.addEventListener('load', hideLoading);
91
91
 
92
92
  </script>
93
93
  </body>
94
- </html>
94
+ </html>
@@ -8,7 +8,7 @@
8
8
  padding-bottom: 2rem;
9
9
  }
10
10
  </style>
11
- {% endblock %}
11
+ {% endblock %}
12
12
 
13
13
  {% block navbar %}
14
14
  {% include "components/navbar.html" %}
@@ -54,4 +54,4 @@
54
54
  });
55
55
  </script>
56
56
  {% block page_scripts %}{% endblock %}
57
- {% endblock %}
57
+ {% endblock %}
@@ -28,14 +28,14 @@
28
28
  <label>{% trans "End Date" %}</label>
29
29
  </div>
30
30
  </div>
31
-
31
+
32
32
  <!-- Custom Uploader Dropdown with Avatar -->
33
33
  <div class="s12 m6 l3">
34
34
  <div class="field label border round suffix relative" onclick="toggleUploaderMenu(event)">
35
35
  <input type="text" id="uploader-display" readonly value="{{ request.GET.uploader|default:'' }}" placeholder="{% trans 'All Uploaders' %}" style="cursor: pointer;">
36
36
  <label>{% trans "Uploader" %}</label>
37
37
  <i class="chevron-down">arrow_drop_down</i>
38
-
38
+
39
39
  <div id="uploader-menu" class="absolute surface elevate round left top-round bottom-round scroll" style="top: 100%; left: 0; right: 0; max-height: 300px; display: none; z-index: 20;">
40
40
  <a class="row padding hover pointer" onclick="selectUploader('', '{% trans 'All Uploaders' %}')">
41
41
  <span>{% trans "All Uploaders" %}</span>
@@ -82,4 +82,4 @@
82
82
  </div>
83
83
  </div>
84
84
  </div>
85
- </form>
85
+ </form>
@@ -5,7 +5,7 @@
5
5
  const portionsInput = document.getElementById('portions');
6
6
  const ingredientsDisplay = document.getElementById('ingredients-display');
7
7
  const selector = document.querySelector('.portion-selector');
8
-
8
+
9
9
  if (!portionsInput || !ingredientsDisplay || !selector) return;
10
10
 
11
11
  const recipeId = selector.dataset.recipeId;
@@ -17,11 +17,11 @@
17
17
  const url = `/api/v1/recipes/${recipeId}/scale-ingredients?target_servings=${targetServings}`;
18
18
  console.log("Fetching:", url);
19
19
  const response = await fetch(url);
20
-
20
+
21
21
  if (!response.ok) throw new Error(`Status: ${response.status}`);
22
-
22
+
23
23
  const data = await response.json();
24
-
24
+
25
25
  let html = '<div class="padding">';
26
26
  data.forEach(item => {
27
27
  html += `<div class="mb-1">${item.scaled_line}</div>`;
@@ -47,4 +47,4 @@
47
47
  document.addEventListener('DOMContentLoaded', () => {
48
48
  scaleIngredients();
49
49
  });
50
- </script>
50
+ </script>
@@ -13,7 +13,7 @@
13
13
  <button class="circle transparent" data-ui="#language-menu">
14
14
  <i>language</i>
15
15
  </button>
16
-
16
+
17
17
  {% if user.is_authenticated %}
18
18
  {% if user.avatar %}
19
19
  <img src="{{ user.avatar.url }}" class="circle" data-ui="#user-menu">
@@ -21,7 +21,7 @@
21
21
  <img src="https://www.w3schools.com/howto/img_avatar.png" class="circle" data-ui="#user-menu">
22
22
  {% endif %}
23
23
  {% else %}
24
- <a href="{% url 'admin:login' %}"><button class="chip transparent border white-text">{% trans "Login" %}</button></a>
24
+ <a href="{% url 'login' %}"><button class="chip transparent border white-text">{% trans "Login" %}</button></a>
25
25
  <a href="{% url 'signup' %}"><button class="chip primary">{% trans "Sign up" %}</button></a>
26
26
  {% endif %}
27
27
  </nav>
@@ -19,17 +19,17 @@
19
19
  <p class="small-text ml-2">{{ user_rating.comment }}</p>
20
20
  {% endif %}
21
21
  {% endif %}
22
-
22
+
23
23
  <form method="post" action="{% url 'recipe_rate' pk=recipe.pk %}">
24
24
  {% csrf_token %}
25
25
  <label class="bold">{% trans "Rate this sandwich" %}</label>
26
26
  <div class="field middle-align">
27
27
  <label class="slider">
28
- <input type="range"
29
- name="{{ rating_form.score.name }}"
30
- min="0"
31
- max="10"
32
- step="0.1"
28
+ <input type="range"
29
+ name="{{ rating_form.score.name }}"
30
+ min="0"
31
+ max="10"
32
+ step="0.1"
33
33
  value="{{ user_rating.score|default:'5.0' }}"
34
34
  oninput="document.getElementById('score-output').innerText = parseFloat(this.value).toFixed(1)">
35
35
  <span></span>
@@ -44,7 +44,7 @@
44
44
  </form>
45
45
  </article>
46
46
  {% else %}
47
- <p><a href="{% url 'admin:login' %}" class="link">{% trans "Log in" %}</a> {% trans "to rate this recipe." %}</p>
47
+ <p><a href="{% url 'login' %}" class="link">{% trans "Log in" %}</a> {% trans "to rate this recipe." %}</p>
48
48
  {% endif %}
49
49
 
50
50
  {% if all_ratings %}
@@ -63,4 +63,4 @@
63
63
  </div>
64
64
  {% endfor %}
65
65
  </div>
66
- {% endif %}
66
+ {% endif %}
@@ -28,14 +28,14 @@
28
28
  <label>{% trans "End Date" %}</label>
29
29
  </div>
30
30
  </div>
31
-
31
+
32
32
  <!-- Custom Uploader Dropdown with Avatar -->
33
33
  <div class="s12 m6 l3">
34
34
  <div class="field label border round suffix relative" onclick="toggleUploaderMenu(event)">
35
35
  <input type="text" id="uploader-display" readonly value="{{ request.GET.uploader|default:'' }}" placeholder="{% trans 'All Uploaders' %}" style="cursor: pointer;">
36
36
  <label>{% trans "Uploader" %}</label>
37
37
  <i class="chevron-down">arrow_drop_down</i>
38
-
38
+
39
39
  <div id="uploader-menu" class="absolute surface elevate round left top-round bottom-round scroll" style="top: 100%; left: 0; right: 0; max-height: 300px; display: none; z-index: 20;">
40
40
  <a class="row padding hover pointer" onclick="selectUploader('', '{% trans 'All Uploaders' %}')">
41
41
  <span>{% trans "All Uploaders" %}</span>
@@ -22,12 +22,12 @@
22
22
  function selectUploader(username, displayName) {
23
23
  document.getElementById('uploader-hidden').value = username;
24
24
  document.getElementById('uploader-display').value = username ? username : ''; // Or displayName
25
-
25
+
26
26
  // Trigger HTMX
27
27
  document.body.dispatchEvent(new Event('uploaderChange'));
28
-
28
+
29
29
  // Close menu (handled by toggle or outside click)
30
- // document.getElementById('uploader-menu').style.display = 'none';
30
+ // document.getElementById('uploader-menu').style.display = 'none';
31
31
  }
32
32
 
33
33
  function toggleTagMenu(event) {
@@ -45,10 +45,10 @@
45
45
  var checkboxes = menu.querySelectorAll('input[type="checkbox"]');
46
46
  var hiddenContainer = document.getElementById('tag-hidden-container');
47
47
  var displayInput = document.getElementById('tag-display');
48
-
48
+
49
49
  hiddenContainer.innerHTML = ''; // Clear existing
50
50
  var selectedNames = [];
51
-
51
+
52
52
  checkboxes.forEach(function(cb) {
53
53
  if (cb.checked) {
54
54
  selectedNames.push(cb.value);
@@ -59,7 +59,7 @@
59
59
  hiddenContainer.appendChild(input);
60
60
  }
61
61
  });
62
-
62
+
63
63
  if (selectedNames.length > 0) {
64
64
  displayInput.value = selectedNames.join(', ');
65
65
  } else {
@@ -95,4 +95,4 @@
95
95
  }
96
96
  }
97
97
  });
98
- </script>
98
+ </script>
@@ -21,7 +21,7 @@
21
21
  text-align: center;
22
22
  }
23
23
  </style>
24
- {% load custom_filters %} {# Load your custom filters #}
24
+ {% load custom_filters %} {# Load your custom filters #}
25
25
  <script type="application/ld+json">
26
26
  {
27
27
  "@context": "https://schema.org",
@@ -68,7 +68,7 @@
68
68
 
69
69
  {% block content %}
70
70
 
71
- {% load i18n markdown_extras %} {# Keep existing load tags #}
71
+ {% load i18n markdown_extras %} {# Keep existing load tags #}
72
72
 
73
73
  <div class="space"></div>
74
74
 
@@ -86,20 +86,20 @@
86
86
  <div class="s12 m12 l5">
87
87
  <article class="round no-padding elevate">
88
88
  {% if recipe.image %}
89
- <img src="{{ recipe.image_large.url }}"
90
- srcset="{{ recipe.image_medium.url }} 700w, {{ recipe.image_large.url }} 1200w"
89
+ <img src="{{ recipe.image_large.url }}"
90
+ srcset="{{ recipe.image_medium.url }} 700w, {{ recipe.image_large.url }} 1200w"
91
91
  sizes="(max-width: 768px) 95vw, 600px"
92
- alt="{{ recipe.title }}"
92
+ alt="{{ recipe.title }}"
93
93
  loading="lazy"
94
94
  class="responsive top-round">
95
95
  {% else %}
96
96
  <div class="primary medium-height top-round middle-align center-align" style="height:300px;">
97
97
  <i class="extra">lunch_dining</i>
98
98
  </div>
99
- {% endif %}
99
+ {% endif %}
100
100
  <div class="padding">
101
101
  {% include "components/recipe_header.html" %}
102
-
102
+
103
103
  <div class="space"></div>
104
104
  <div class="divider"></div>
105
105
  <div class="space"></div>
@@ -130,7 +130,7 @@
130
130
  <div class="space"></div>
131
131
  <div class="divider"></div>
132
132
  <div class="space"></div>
133
-
133
+
134
134
  {% include "components/rating_section.html" %}
135
135
 
136
136
  {% if user.is_authenticated and user.is_staff %}
@@ -147,7 +147,7 @@
147
147
  <!-- Right Column: Details -->
148
148
  <div class="s12 m12 l7">
149
149
  <div class="padding">
150
-
150
+
151
151
  <h5 class="primary-text">{% trans "Description" %}</h5>
152
152
  <div class="large-text">
153
153
  {% if recipe.description %}
@@ -156,9 +156,9 @@
156
156
  <p class="italic">{% trans "No description yet." %}</p>
157
157
  {% endif %}
158
158
  </div>
159
-
159
+
160
160
  <div class="large-space"></div>
161
-
161
+
162
162
  {% include "components/ingredients_section.html" %}
163
163
 
164
164
  <div class="large-space"></div>
@@ -39,4 +39,4 @@
39
39
 
40
40
  {% block page_scripts %}
41
41
  {% include "components/search_scripts.html" %}
42
- {% endblock %}
42
+ {% endblock %}
@@ -0,0 +1,57 @@
1
+ {% extends "base_beer.html" %}
2
+ {% load static i18n %}
3
+ {% block title %}{% trans "Login" %}{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="large-space"></div>
7
+
8
+ <div class="grid">
9
+ <div class="s12 m10 l8 xl6 middle-align center-align" style="margin: 0 auto;">
10
+ <article class="round elevate">
11
+ <div class="padding">
12
+ <h4 class="center-align primary-text">{% trans "Login to your account" %}</h4>
13
+ <p class="center-align">{% trans "Welcome back!" %}</p>
14
+ </div>
15
+
16
+ <form method="post" novalidate>
17
+ {% csrf_token %}
18
+
19
+ {% if form.non_field_errors %}
20
+ <div class="padding error surface round mb">
21
+ {% for error in form.non_field_errors %}
22
+ <div class="row align-center">
23
+ <i class="error-text">warning</i>
24
+ <span class="error-text">{{ error }}</span>
25
+ </div>
26
+ {% endfor %}
27
+ </div>
28
+ {% endif %}
29
+
30
+ <div class="field label border round {% if form.username.errors %}error{% endif %}">
31
+ <input type="text" name="{{ form.username.name }}" id="{{ form.username.id_for_label }}" value="{{ form.username.value|default:'' }}">
32
+ <label>{% trans "Username" %}</label>
33
+ {% if form.username.errors %}
34
+ <span class="helper error-text">{{ form.username.errors.0 }}</span>
35
+ {% endif %}
36
+ </div>
37
+
38
+ <div class="field label border round {% if form.password.errors %}error{% endif %}">
39
+ <input type="password" name="{{ form.password.name }}" id="{{ form.password.id_for_label }}">
40
+ <label>{% trans "Password" %}</label>
41
+ {% if form.password.errors %}
42
+ <span class="helper error-text">{{ form.password.errors.0 }}</span>
43
+ {% endif %}
44
+ </div>
45
+
46
+ <div class="large-space"></div>
47
+
48
+ <nav class="right-align">
49
+ <a class="button transparent border round" href="{% url 'index' %}">{% trans "Cancel" %}</a>
50
+ <button type="submit" class="button primary round">{% trans "Login" %}</button>
51
+ </nav>
52
+
53
+ </form>
54
+ </article>
55
+ </div>
56
+ </div>
57
+ {% endblock %}
@@ -11,7 +11,7 @@
11
11
  <i class="extra">lunch_dining</i>
12
12
  </div>
13
13
  {% endif %}
14
-
14
+
15
15
  {% if user.is_authenticated %}
16
16
  <div class="absolute top left padding">
17
17
  <a href="{% url 'toggle_favorite' recipe.pk %}" class="button circle surface white-text">
@@ -22,10 +22,10 @@
22
22
  </div>
23
23
  {% endif %}
24
24
  </div>
25
-
25
+
26
26
  <div class="padding">
27
27
  <h5 class="truncate pointer" onclick="location.href='{% url 'recipe_detail' recipe.slug %}';">{{ recipe.title }}</h5>
28
-
28
+
29
29
  <div class="row align-center">
30
30
  {% if recipe.uploaded_by %}
31
31
  <a href="{% url 'index' %}?uploader={{ recipe.uploaded_by.username|urlencode }}" class="row align-center" style="color: inherit; text-decoration: none;">
@@ -41,7 +41,7 @@
41
41
  <span class="tiny-text left-margin">{% trans "Unknown" %}</span>
42
42
  {% endif %}
43
43
  </div>
44
-
44
+
45
45
  <div class="space"></div>
46
46
 
47
47
  <div class="row wrap">
@@ -57,14 +57,14 @@
57
57
  </div>
58
58
 
59
59
  <div class="space"></div>
60
-
60
+
61
61
  <div class="row wrap">
62
62
  {% for tag in recipe.tags.all %}
63
63
  <a href="{% url 'index' %}?tag={{ tag.name|urlencode }}" class="chip small round surface margin-bottom">{{ tag.name }}</a>
64
64
  {% endfor %}
65
65
  </div>
66
66
  </div>
67
-
67
+
68
68
  <div class="padding pt-0">
69
69
  <a href="{% url 'recipe_detail' recipe.slug %}" class="button fill round responsive">
70
70
  <span>{% trans 'View Recipe' %}</span>
@@ -84,4 +84,4 @@
84
84
  {% endif %}
85
85
  </div>
86
86
  {% endfor %}
87
- </div>
87
+ </div>
@@ -15,15 +15,15 @@
15
15
  {% endif %}
16
16
  </h4>
17
17
  <div class="space"></div>
18
-
18
+
19
19
  <form method="post" enctype="multipart/form-data">
20
20
  {% csrf_token %}
21
-
21
+
22
22
  <div class="grid">
23
23
  <div class="s12">
24
24
  <div class="field label border round">
25
25
  {{ form.title }}
26
- <label>{% trans "Title" %}</label>
26
+ <label>{% trans "Title" %}</label>
27
27
  </div>
28
28
  {% if form.title.errors %}
29
29
  <p class="error-text tiny-text no-margin">{{ form.title.errors.0 }}</p>
@@ -77,7 +77,7 @@
77
77
  </div>
78
78
 
79
79
  <div class="large-space"></div>
80
-
80
+
81
81
  <div class="row right-align">
82
82
  <!-- Fallback to index if referer is missing, or history.back -->
83
83
  <a href="javascript:history.back()" class="button transparent border round">{% trans "Cancel" %}</a>
@@ -116,4 +116,4 @@
116
116
  }
117
117
  }
118
118
  </script>
119
- {% endblock %}
119
+ {% endblock %}
@@ -19,10 +19,10 @@
19
19
  {% trans "After creating the account you will be logged in and redirected to the admin." %}
20
20
  </p>
21
21
  </div>
22
-
22
+
23
23
  <form method="post" novalidate>
24
24
  {% csrf_token %}
25
-
25
+
26
26
  {% if form.non_field_errors %}
27
27
  <div class="padding error surface round mb">
28
28
  {% for err in form.non_field_errors %}
@@ -70,7 +70,7 @@
70
70
  <span class="helper error-text">{{ form.email.errors.0 }}</span>
71
71
  {% endif %}
72
72
  </div>
73
-
73
+
74
74
  <div class="space"></div>
75
75
  <div class="divider"></div>
76
76
  <div class="space"></div>
@@ -92,7 +92,7 @@
92
92
  </div>
93
93
 
94
94
  <div class="large-space"></div>
95
-
95
+
96
96
  <nav class="right-align">
97
97
  <a class="button transparent border round" href="{% url 'index' %}">{% trans "Cancel" %}</a>
98
98
  <button type="submit" class="button primary round">{% trans "Create admin" %}</button>
@@ -12,10 +12,10 @@
12
12
  <h4 class="center-align primary-text">{% trans "Create your account" %}</h4>
13
13
  <p class="center-align">{% trans "Join the Sandwitches community today!" %}</p>
14
14
  </div>
15
-
15
+
16
16
  <form method="post" enctype="multipart/form-data" novalidate>
17
17
  {% csrf_token %}
18
-
18
+
19
19
  {% if form.non_field_errors %}
20
20
  <div class="padding error surface round mb">
21
21
  {% for err in form.non_field_errors %}
@@ -63,7 +63,7 @@
63
63
  <span class="helper error-text">{{ form.email.errors.0 }}</span>
64
64
  {% endif %}
65
65
  </div>
66
-
66
+
67
67
  <div class="field label border round {% if form.language.errors %}error{% endif %}">
68
68
  <select name="{{ form.language.name }}" id="{{ form.language.id_for_label }}">
69
69
  {% for value, label in form.language.field.choices %}
@@ -83,7 +83,7 @@
83
83
  <span class="helper error-text">{{ form.bio.errors.0 }}</span>
84
84
  {% endif %}
85
85
  </div>
86
-
86
+
87
87
  <div class="field middle-align {% if form.avatar.errors %}error{% endif %}">
88
88
  <nav>
89
89
  <div class="max">
@@ -120,7 +120,7 @@
120
120
  </div>
121
121
 
122
122
  <div class="large-space"></div>
123
-
123
+
124
124
  <nav class="right-align">
125
125
  <a class="button transparent border round" href="{% url 'index' %}">{% trans "Cancel" %}</a>
126
126
  <button type="submit" class="button primary round">{% trans "Sign Up" %}</button>
@@ -130,4 +130,4 @@
130
130
  </article>
131
131
  </div>
132
132
  </div>
133
- {% endblock %}
133
+ {% endblock %}
sandwitches/urls.py CHANGED
@@ -21,6 +21,7 @@ from . import views
21
21
  from .api import api
22
22
  from django.conf.urls.i18n import i18n_patterns
23
23
  from .feeds import LatestRecipesFeed # Import the feed class
24
+ from django.contrib.auth.views import LogoutView # Import LogoutView
24
25
 
25
26
 
26
27
  import os
@@ -30,6 +31,8 @@ import sys
30
31
  urlpatterns = [
31
32
  path("i18n/", include("django.conf.urls.i18n")),
32
33
  path("signup/", views.signup, name="signup"),
34
+ path("login/", views.CustomLoginView.as_view(), name="login"),
35
+ path("logout/", LogoutView.as_view(next_page="index"), name="logout"),
33
36
  path("admin/", admin.site.urls),
34
37
  path("api/", api.urls),
35
38
  path("media/<path:file_path>", views.media, name="media"),
sandwitches/views.py CHANGED
@@ -23,12 +23,19 @@ import mimetypes
23
23
  from PIL import Image
24
24
  from django.db.models import Q, Avg
25
25
  from django_tasks.backends.database.models import DBTaskResult
26
+ from django.contrib.auth.views import LoginView
27
+
26
28
 
27
29
  from sandwitches import __version__ as sandwitches_version
28
30
 
29
31
  User = get_user_model()
30
32
 
31
33
 
34
+ class CustomLoginView(LoginView):
35
+ template_name = "login.html"
36
+ redirect_authenticated_user = True
37
+
38
+
32
39
  @staff_member_required
33
40
  def admin_dashboard(request):
34
41
  recipe_count = Recipe.objects.count() # ty:ignore[unresolved-attribute]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sandwitches
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Add your description here
5
5
  Author: Martyn van Dijke
6
6
  Author-email: Martyn van Dijke <martijnvdijke600@gmail.com>
@@ -3,7 +3,7 @@ sandwitches/admin.py,sha256=3n73AD-j7vyajgoFdhPe3IXH6SFGIAq378gKDRr6ims,1852
3
3
  sandwitches/api.py,sha256=4Upjd78dHVZDOIFkZjPst0mLfVdiToB3RVVrBpttTYg,5714
4
4
  sandwitches/asgi.py,sha256=cygnXdXSSVspM7ZXuj47Ef6oz7HSTw4D7BPzgE2PU5w,399
5
5
  sandwitches/feeds.py,sha256=iz1d11dV0utA0ZNsB7VIAp0h8Zr5mFNSKJWHbw_j6YM,683
6
- sandwitches/forms.py,sha256=ODrwV52TSBxIatmVp_5A_l7WcDh9m1D3vLgL22HwLHo,5880
6
+ sandwitches/forms.py,sha256=QMSCpiy42AjretzEkj8FghfmBj-gkalSOXZVwiTtkOQ,5916
7
7
  sandwitches/locale/nl/LC_MESSAGES/django.mo,sha256=EzQWzIhz_Na3w9AS7F-YjB-Xv63t4sMRSAkEQ1-g32M,5965
8
8
  sandwitches/locale/nl/LC_MESSAGES/django.po,sha256=znxspEoMwkmktusZtbVrt1KG1LDUwIEi4ZEIE3XGeoI,25904
9
9
  sandwitches/migrations/0001_initial.py,sha256=hXnCAhoA91C6YCinXyUdIfQ7QL29NPBHFfTqLgulMsY,12507
@@ -15,48 +15,49 @@ sandwitches/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
15
15
  sandwitches/models.py,sha256=MHx892trckPaIM-K5u8fFGj8HuLHi7SnPG5COP94Ung,7196
16
16
  sandwitches/settings.py,sha256=av4Cjplj0pwfThvaNfxPXh7D-0GCE4y_smtcH_mVYWE,5761
17
17
  sandwitches/storage.py,sha256=ibBG6tVtArqzgEKsRimZPwsqW7i9j4WiPLLHrOJchow,3578
18
- sandwitches/tasks.py,sha256=8jHQougtywhsDyd-6W4VV-x5PCwwhNG3J99E0hzy_MA,3660
19
- sandwitches/templates/admin/admin_base.html,sha256=saeKUBngPhkq2udPXuO8oQPEu4RqgJaY0cic_QLIVGo,4263
20
- sandwitches/templates/admin/confirm_delete.html,sha256=ewP0rRxbeSst5x5ZmLyAbhUiSArevnN7VtkR00VdPMc,812
21
- sandwitches/templates/admin/dashboard.html,sha256=9dfmXotisr1vZw88eHbnKC_k-3ZGewbixZrJMpHj-pI,6167
18
+ sandwitches/tasks.py,sha256=rRQpWZp-vDbMgVmnZAz8GaPzK7F3uzAr6lho7Zq-mdM,3656
19
+ sandwitches/templates/admin/admin_base.html,sha256=yPFo1K1LrRuPKfOuOGLEgQm29vWqzvkSoDNlpmxYPaY,4261
20
+ sandwitches/templates/admin/confirm_delete.html,sha256=HfsZI_gV8JQTKz215TYgPWBrgrFhGv1UB3N-0Hln-14,804
21
+ sandwitches/templates/admin/dashboard.html,sha256=uHisWI3x-KehPDlwBFF5bIT5-tPsCVU5duA_ZPF-GHc,6168
22
22
  sandwitches/templates/admin/rating_list.html,sha256=ZTxu4emeYInlYyw7b19c8cYlj90fo1wtA-1BEJKZGt0,1256
23
- sandwitches/templates/admin/recipe_form.html,sha256=VWwfVgmG8oC3nRVVCtVDLop0LS9ctdy6-QhbfhQ7k8I,7617
23
+ sandwitches/templates/admin/recipe_form.html,sha256=ssLnS63UobRFwPbDHMrnwniJEta3QHkWOJb8IPzYd_k,7560
24
24
  sandwitches/templates/admin/recipe_list.html,sha256=HDOmhK-XEPwTv1CCBqTXK79pLoH5y2xKPFjjue0HFeQ,2454
25
25
  sandwitches/templates/admin/tag_form.html,sha256=JRWgAl4fz_Oy-Kuo1K6Mex_CXdsHMABzzyPazthr1Kg,989
26
26
  sandwitches/templates/admin/tag_list.html,sha256=ttxwXgfdxkEs4Cmrz5RHaGmaqLd7JDmWhjv80XIQqyw,1246
27
- sandwitches/templates/admin/task_detail.html,sha256=1tU57R_EBw6v7PKvY7Ucf6BJYDbf_K9KXY_0qq2dHEA,3861
27
+ sandwitches/templates/admin/task_detail.html,sha256=dO5zHOG-yTY1ly3VPA3o3jjie0wmxw0gdddtSKpN-W8,3825
28
28
  sandwitches/templates/admin/task_list.html,sha256=3YF7YQ3nbXnWjApKeA07Z7HkhdMuH4s6sLoN7gwg0eE,1535
29
29
  sandwitches/templates/admin/user_form.html,sha256=7_6GShLROFeJJexL1XLFUXdW9_lYF87eT6cigB5bQo4,1314
30
30
  sandwitches/templates/admin/user_list.html,sha256=6O1YctULY-tqJnagybJof9ERA_NL1LX_a8cAu6_aWVQ,2193
31
- sandwitches/templates/base.html,sha256=Z_odI29O3HkCKUN0Wk3ETdWOfC5QD7FIHp0i5Xr0eoU,3218
32
- sandwitches/templates/base_beer.html,sha256=gHangI-Q089WqLv7gfboInKGgyUbQt0H5Rqlb-dP6Ls,2077
33
- sandwitches/templates/components/favorites_search_form.html,sha256=vX-yG7QYxfWsJUTjLrazenkDPZAHosdHGY76RWg19UI,5249
31
+ sandwitches/templates/base.html,sha256=07zDXWzRC-aptZW6oq3xcxvDA1Lh87F7kTpBeYFUwsg,3211
32
+ sandwitches/templates/base_beer.html,sha256=4QgU4_gu_RRMtimmRAhATDJ3mj_WANxtilQJYNgAL60,2077
33
+ sandwitches/templates/components/favorites_search_form.html,sha256=tpD8SpS47TUDJBwxhMuvjhTN9pjWoRGFW50TBv48Ld4,5202
34
34
  sandwitches/templates/components/footer.html,sha256=Qk-myRtXS6-1b3fMowVGnSuFb_UkUgX6BYX9zgh_SV8,486
35
- sandwitches/templates/components/ingredients_scripts.html,sha256=BaWiNbWOYarqZOIkdR8yfv-bwQ4OdfHg0GOOCzGyZO8,1837
35
+ sandwitches/templates/components/ingredients_scripts.html,sha256=2zKTC65GYF589uW1sCpDq0jOkS3BnsuOwUpJbbigrn4,1794
36
36
  sandwitches/templates/components/ingredients_section.html,sha256=XsaVXTs9MIwjfJeLjlzah3GWWj8oFU-_HJd9i9l1HAo,665
37
37
  sandwitches/templates/components/instructions_section.html,sha256=RFlA4uPiI6vf1e2QgiD5KzGoy7Vg7y7nFY7TFabCYLA,277
38
38
  sandwitches/templates/components/language_dialog.html,sha256=iz-6QhFe4f_dsVhGDhVx6KKKLgQz4grX8tbIqSQjDsg,1184
39
- sandwitches/templates/components/navbar.html,sha256=8ugRB2g_pI4VohCq3yj8yrPnKr8t3Q440G3VDtKG7vo,1057
40
- sandwitches/templates/components/rating_section.html,sha256=Z_YDkmcSC1sNN5LR-3pnnb4gRB-SO95XBY3dbnINnBc,2733
39
+ sandwitches/templates/components/navbar.html,sha256=CtWqAmPke7nm-UqSvGGl6TPaNwT2Zqkw9UySOCA8hfo,1049
40
+ sandwitches/templates/components/rating_section.html,sha256=8O5IsFfQwnElMQZLnDpJiuCvvQMLa3jCS67u_RhMi7o,2717
41
41
  sandwitches/templates/components/recipe_header.html,sha256=-LCp6KqkQO7a7yqR0jKUQ95-RczYU-_MFO058lE04_w,1473
42
- sandwitches/templates/components/search_form.html,sha256=OqsrpG-eKtSedZ9k0ee1WrezOokuESrvKi62U1VA-B4,6665
43
- sandwitches/templates/components/search_scripts.html,sha256=qDIqcOg3QEWS5bfJkJ3zFmWpt1UOiLs_vahOc-MtxZs,3463
42
+ sandwitches/templates/components/search_form.html,sha256=B8579Jo44gLrlmvkkc2-Vuv_QD93Ljt6F2J1WgTDV60,6617
43
+ sandwitches/templates/components/search_scripts.html,sha256=HvsO5e50DoTZeoFiYeNP5S8S5h7Zfr9VULOWKKR1i_M,3423
44
44
  sandwitches/templates/components/side_menu.html,sha256=JyrhmyWLKKZj1yqriHqQc6Fv25TpmIgXNhoEdxrePVQ,1529
45
45
  sandwitches/templates/components/user_menu.html,sha256=c20cBpyLheGvHdQ5nn-c4fjNlhfnAt3FAsw1V46rTwQ,369
46
- sandwitches/templates/detail.html,sha256=w3e8tLZSbdHv3PVrzo-M1MSOsZPUbJCmvvrCYLiYhxk,5709
46
+ sandwitches/templates/detail.html,sha256=g-O_RsW9Ix9ivWC0nZ4FwHY2NhgYZ3bEGLpqGY0JSxg,5642
47
47
  sandwitches/templates/favorites.html,sha256=0cPpW07N6Isrb8XpvA5Eh97L2-12QFZ43EzeJvbOlXo,917
48
- sandwitches/templates/index.html,sha256=_ERrYj2iy_UWyIT_xVPNtBEggXDDVMSo61t7vEDdUTY,898
49
- sandwitches/templates/partials/recipe_list.html,sha256=QoQNx1FP7viRgTp7Bpe20ECB9EZRt6vX-6jGxjd3WAQ,4001
50
- sandwitches/templates/recipe_form.html,sha256=7-w5o3lfTJB3bTpL7UuMsin8ruHahaCXehp_RzeWjCA,4786
51
- sandwitches/templates/setup.html,sha256=CdcTrCwVmWMc-Iz1680MJp8r70Nt5YrYp4RYqLfB-LM,4691
52
- sandwitches/templates/signup.html,sha256=GKzTZVczL6ovmAK2wT1t7VTIJgwotS16lsOC66AnYHc,6213
48
+ sandwitches/templates/index.html,sha256=JqIeAY2Yz5yZ9MWSMjWXOQIohykcPlJHBRUrspahGCU,899
49
+ sandwitches/templates/login.html,sha256=LiQskhkOkfx0EE4ssA1ToqQ3oEll08OPYLDIkLjHfU8,2177
50
+ sandwitches/templates/partials/recipe_list.html,sha256=AVzz_fIE4cBVU-E2E0VpE1tFmUV8jy7QpOninZEUMi8,3952
51
+ sandwitches/templates/recipe_form.html,sha256=TzdlzeSqUoJTDTuCdFbeF5jCBZUTZEtlTIC_IQM3WL8,4754
52
+ sandwitches/templates/setup.html,sha256=iNveFgePATsCSO4XMbGPa8TnWHyvj8S_5WwcW6i7pbo,4661
53
+ sandwitches/templates/signup.html,sha256=pNBSlRGZI_B5ccF3dWpUgWBcjODkdLlq7HhyJLYIHCI,6176
53
54
  sandwitches/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
55
  sandwitches/templatetags/custom_filters.py,sha256=0KDFlFz4b5LwlcURBAmzyYWKKea-LwydZytJGVkkuKA,243
55
56
  sandwitches/templatetags/markdown_extras.py,sha256=0ibmRzxE3r85x4k7kK71R-9UT0CgeegYF7MHzj3juTI,344
56
- sandwitches/urls.py,sha256=uhzsVpKC8Vo6ReDZk3BMg9Pxg4uSrFO8rUdNm18h-xI,3710
57
+ sandwitches/urls.py,sha256=fT4JYLMrTS1IDgb3Ruivj69lXcW86Th44Lt4Kr6Tchs,3922
57
58
  sandwitches/utils.py,sha256=SJP-TkeRZ0OIfaMigYrOSbxRqYXswoqoWhwll3nFuAM,7245
58
- sandwitches/views.py,sha256=BpJkrBVXYoDMFborpH0FEaYT5xk0Dg4adCurXbFl4-o,20215
59
+ sandwitches/views.py,sha256=DA2q1ZrzdimTyVGt9Sbnvvex20gzMuu3N4SGoLQFNtU,20372
59
60
  sandwitches/wsgi.py,sha256=Eyncpnahq_4s3Lr9ruB-R3Lu9j9zBXqgPbUj7qhIbwU,399
60
- sandwitches-2.1.0.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
61
- sandwitches-2.1.0.dist-info/METADATA,sha256=DKBfQ5kBoZjDLbj-7y1TqnIRIcpaBLjib3V989q-q5Q,3111
62
- sandwitches-2.1.0.dist-info/RECORD,,
61
+ sandwitches-2.1.2.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
62
+ sandwitches-2.1.2.dist-info/METADATA,sha256=TH8TEmijKUIiOWXUBs-WBv8hZestoOqw0NldlLLJ6kM,3111
63
+ sandwitches-2.1.2.dist-info/RECORD,,