sandwitches 2.2.0__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/__init__.py +6 -0
- sandwitches/admin.py +69 -0
- sandwitches/api.py +207 -0
- sandwitches/asgi.py +16 -0
- sandwitches/feeds.py +23 -0
- sandwitches/forms.py +196 -0
- sandwitches/locale/nl/LC_MESSAGES/django.mo +0 -0
- sandwitches/locale/nl/LC_MESSAGES/django.po +1010 -0
- sandwitches/migrations/0001_initial.py +328 -0
- sandwitches/migrations/0002_historicalrecipe_servings_recipe_servings.py +27 -0
- sandwitches/migrations/0003_setting.py +35 -0
- sandwitches/migrations/0004_alter_setting_ai_api_key_and_more.py +37 -0
- sandwitches/migrations/0005_rating_comment.py +17 -0
- sandwitches/migrations/0006_historicalrecipe_is_highlighted_and_more.py +22 -0
- sandwitches/migrations/__init__.py +0 -0
- sandwitches/models.py +218 -0
- sandwitches/settings.py +220 -0
- sandwitches/storage.py +114 -0
- sandwitches/tasks.py +115 -0
- sandwitches/templates/admin/admin_base.html +118 -0
- sandwitches/templates/admin/confirm_delete.html +23 -0
- sandwitches/templates/admin/dashboard.html +262 -0
- sandwitches/templates/admin/rating_list.html +38 -0
- sandwitches/templates/admin/recipe_form.html +184 -0
- sandwitches/templates/admin/recipe_list.html +64 -0
- sandwitches/templates/admin/tag_form.html +30 -0
- sandwitches/templates/admin/tag_list.html +37 -0
- sandwitches/templates/admin/task_detail.html +91 -0
- sandwitches/templates/admin/task_list.html +41 -0
- sandwitches/templates/admin/user_form.html +37 -0
- sandwitches/templates/admin/user_list.html +60 -0
- sandwitches/templates/base.html +94 -0
- sandwitches/templates/base_beer.html +57 -0
- sandwitches/templates/components/carousel_scripts.html +59 -0
- sandwitches/templates/components/favorites_search_form.html +85 -0
- sandwitches/templates/components/footer.html +14 -0
- sandwitches/templates/components/ingredients_scripts.html +50 -0
- sandwitches/templates/components/ingredients_section.html +11 -0
- sandwitches/templates/components/instructions_section.html +9 -0
- sandwitches/templates/components/language_dialog.html +26 -0
- sandwitches/templates/components/navbar.html +27 -0
- sandwitches/templates/components/rating_section.html +66 -0
- sandwitches/templates/components/recipe_header.html +32 -0
- sandwitches/templates/components/search_form.html +106 -0
- sandwitches/templates/components/search_scripts.html +98 -0
- sandwitches/templates/components/side_menu.html +35 -0
- sandwitches/templates/components/user_menu.html +10 -0
- sandwitches/templates/detail.html +178 -0
- sandwitches/templates/favorites.html +42 -0
- sandwitches/templates/index.html +76 -0
- sandwitches/templates/login.html +57 -0
- sandwitches/templates/partials/recipe_list.html +87 -0
- sandwitches/templates/recipe_form.html +119 -0
- sandwitches/templates/setup.html +105 -0
- sandwitches/templates/signup.html +133 -0
- sandwitches/templatetags/__init__.py +0 -0
- sandwitches/templatetags/custom_filters.py +15 -0
- sandwitches/templatetags/markdown_extras.py +17 -0
- sandwitches/urls.py +109 -0
- sandwitches/utils.py +222 -0
- sandwitches/views.py +647 -0
- sandwitches/wsgi.py +16 -0
- sandwitches-2.2.0.dist-info/METADATA +104 -0
- sandwitches-2.2.0.dist-info/RECORD +65 -0
- sandwitches-2.2.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
{% extends "admin/admin_base.html" %}
|
|
2
|
+
{% load i18n %}
|
|
3
|
+
|
|
4
|
+
{% block admin_title %}{% trans "Dashboard" %}{% endblock %}
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
{% block content %}
|
|
9
|
+
|
|
10
|
+
<div class="row align-center mb-2">
|
|
11
|
+
|
|
12
|
+
<div class="max"></div>
|
|
13
|
+
|
|
14
|
+
<form method="get" class="row no-space border round padding surface-variant">
|
|
15
|
+
|
|
16
|
+
<div class="field label small transparent">
|
|
17
|
+
|
|
18
|
+
<input type="date" name="start_date" value="{{ start_date }}">
|
|
19
|
+
|
|
20
|
+
<label>{% trans "Start Date" %}</label>
|
|
21
|
+
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div class="divider vertical"></div>
|
|
25
|
+
|
|
26
|
+
<div class="field label small transparent">
|
|
27
|
+
|
|
28
|
+
<input type="date" name="end_date" value="{{ end_date }}">
|
|
29
|
+
|
|
30
|
+
<label>{% trans "End Date" %}</label>
|
|
31
|
+
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<button type="submit" class="button primary square"><i>refresh</i></button>
|
|
35
|
+
|
|
36
|
+
</form>
|
|
37
|
+
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
<div class="grid">
|
|
43
|
+
|
|
44
|
+
<div class="s12 m4">
|
|
45
|
+
|
|
46
|
+
<article class="round primary-container padding">
|
|
47
|
+
|
|
48
|
+
<div class="row align-center">
|
|
49
|
+
|
|
50
|
+
<i class="extra">restaurant</i>
|
|
51
|
+
|
|
52
|
+
<div class="max">
|
|
53
|
+
|
|
54
|
+
<h4 class="bold">{{ recipe_count }}</h4>
|
|
55
|
+
|
|
56
|
+
<div>{% trans "Recipes" %}</div>
|
|
57
|
+
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<nav class="right-align">
|
|
63
|
+
|
|
64
|
+
<a href="{% url 'admin_recipe_list' %}" class="button transparent">{% trans "View all" %}</a>
|
|
65
|
+
|
|
66
|
+
</nav>
|
|
67
|
+
|
|
68
|
+
</article>
|
|
69
|
+
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div class="s12 m4">
|
|
73
|
+
|
|
74
|
+
<article class="round secondary-container padding">
|
|
75
|
+
|
|
76
|
+
<div class="row align-center">
|
|
77
|
+
|
|
78
|
+
<i class="extra">people</i>
|
|
79
|
+
|
|
80
|
+
<div class="max">
|
|
81
|
+
|
|
82
|
+
<h4 class="bold">{{ user_count }}</h4>
|
|
83
|
+
|
|
84
|
+
<div>{% trans "Users" %}</div>
|
|
85
|
+
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<nav class="right-align">
|
|
91
|
+
|
|
92
|
+
<a href="{% url 'admin_user_list' %}" class="button transparent">{% trans "View all" %}</a>
|
|
93
|
+
|
|
94
|
+
</nav>
|
|
95
|
+
|
|
96
|
+
</article>
|
|
97
|
+
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div class="s12 m4">
|
|
101
|
+
|
|
102
|
+
<article class="round tertiary-container padding">
|
|
103
|
+
|
|
104
|
+
<div class="row align-center">
|
|
105
|
+
|
|
106
|
+
<i class="extra">label</i>
|
|
107
|
+
|
|
108
|
+
<div class="max">
|
|
109
|
+
|
|
110
|
+
<h4 class="bold">{{ tag_count }}</h4>
|
|
111
|
+
|
|
112
|
+
<div>{% trans "Tags" %}</div>
|
|
113
|
+
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<nav class="right-align">
|
|
119
|
+
|
|
120
|
+
<a href="{% url 'admin_tag_list' %}" class="button transparent">{% trans "View all" %}</a>
|
|
121
|
+
|
|
122
|
+
</nav>
|
|
123
|
+
|
|
124
|
+
</article>
|
|
125
|
+
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
<!-- Charts Section -->
|
|
131
|
+
|
|
132
|
+
<div class="s12 m6">
|
|
133
|
+
|
|
134
|
+
<article class="round border padding">
|
|
135
|
+
|
|
136
|
+
<h6 class="bold mb-1">{% trans "Recipes Over Time (Last 30 Days)" %}</h6>
|
|
137
|
+
|
|
138
|
+
<canvas id="recipeChart" style="width:100%; max-height:300px;"></canvas>
|
|
139
|
+
|
|
140
|
+
</article>
|
|
141
|
+
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<div class="s12 m6">
|
|
145
|
+
|
|
146
|
+
<article class="round border padding">
|
|
147
|
+
|
|
148
|
+
<h6 class="bold mb-1">{% trans "Average Rating Over Time (Last 30 Days)" %}</h6>
|
|
149
|
+
|
|
150
|
+
<canvas id="ratingChart" style="width:100%; max-height:300px;"></canvas>
|
|
151
|
+
|
|
152
|
+
</article>
|
|
153
|
+
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
<div class="s12">
|
|
159
|
+
|
|
160
|
+
<h5 class="bold">{% trans "Recent Recipes" %}</h5>
|
|
161
|
+
|
|
162
|
+
<table class="border striped no-space">
|
|
163
|
+
|
|
164
|
+
<thead>
|
|
165
|
+
|
|
166
|
+
<tr>
|
|
167
|
+
|
|
168
|
+
<th>{% trans "Title" %}</th>
|
|
169
|
+
|
|
170
|
+
<th>{% trans "Uploader" %}</th>
|
|
171
|
+
|
|
172
|
+
<th>{% trans "Created At" %}</th>
|
|
173
|
+
|
|
174
|
+
<th class="right-align">{% trans "Actions" %}</th>
|
|
175
|
+
|
|
176
|
+
</tr>
|
|
177
|
+
|
|
178
|
+
</thead>
|
|
179
|
+
|
|
180
|
+
<tbody>
|
|
181
|
+
|
|
182
|
+
{% for recipe in recent_recipes %}
|
|
183
|
+
|
|
184
|
+
<tr class="pointer" onclick="location.href='{% url 'admin_recipe_edit' recipe.pk %}'">
|
|
185
|
+
|
|
186
|
+
<td>{{ recipe.title }}</td>
|
|
187
|
+
|
|
188
|
+
<td>{{ recipe.uploaded_by.username|default:"-" }}</td>
|
|
189
|
+
|
|
190
|
+
<td>{{ recipe.created_at|date:"SHORT_DATETIME_FORMAT" }}</td>
|
|
191
|
+
|
|
192
|
+
<td class="right-align">
|
|
193
|
+
|
|
194
|
+
<a href="{% url 'admin_recipe_edit' recipe.pk %}" class="button circle transparent" onclick="event.stopPropagation();"><i>edit</i></a>
|
|
195
|
+
|
|
196
|
+
</td>
|
|
197
|
+
|
|
198
|
+
</tr>
|
|
199
|
+
|
|
200
|
+
{% endfor %}
|
|
201
|
+
|
|
202
|
+
</tbody>
|
|
203
|
+
|
|
204
|
+
</table>
|
|
205
|
+
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
{% endblock %}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
{% block admin_scripts %}
|
|
215
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
216
|
+
<script>
|
|
217
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
218
|
+
const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--primary').trim() || '#5d4037';
|
|
219
|
+
const secondaryColor = getComputedStyle(document.documentElement).getPropertyValue('--secondary').trim() || '#ff7a18';
|
|
220
|
+
|
|
221
|
+
// Recipe Chart
|
|
222
|
+
new Chart(document.getElementById('recipeChart'), {
|
|
223
|
+
type: 'line',
|
|
224
|
+
data: {
|
|
225
|
+
labels: {{ recipe_labels|safe }},
|
|
226
|
+
datasets: [{
|
|
227
|
+
label: '{% trans "Recipes Created" %}',
|
|
228
|
+
data: {{ recipe_counts|safe }},
|
|
229
|
+
borderColor: primaryColor,
|
|
230
|
+
backgroundColor: primaryColor + '33',
|
|
231
|
+
fill: true,
|
|
232
|
+
tension: 0.4
|
|
233
|
+
}]
|
|
234
|
+
},
|
|
235
|
+
options: {
|
|
236
|
+
responsive: true,
|
|
237
|
+
plugins: { legend: { display: false } },
|
|
238
|
+
scales: { y: { beginAtZero: true, ticks: { stepSize: 1 } } }
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Rating Chart
|
|
243
|
+
new Chart(document.getElementById('ratingChart'), {
|
|
244
|
+
type: 'bar',
|
|
245
|
+
data: {
|
|
246
|
+
labels: {{ rating_labels|safe }},
|
|
247
|
+
datasets: [{
|
|
248
|
+
label: '{% trans "Avg Rating" %}',
|
|
249
|
+
data: {{ rating_avgs|safe }},
|
|
250
|
+
backgroundColor: secondaryColor,
|
|
251
|
+
borderRadius: 4
|
|
252
|
+
}]
|
|
253
|
+
},
|
|
254
|
+
options: {
|
|
255
|
+
responsive: true,
|
|
256
|
+
plugins: { legend: { display: false } },
|
|
257
|
+
scales: { y: { min: 0, max: 10 } }
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
</script>
|
|
262
|
+
{% endblock %}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{% extends "admin/admin_base.html" %}
|
|
2
|
+
{% load i18n %}
|
|
3
|
+
|
|
4
|
+
{% block admin_title %}{% trans "Ratings" %}{% endblock %}
|
|
5
|
+
|
|
6
|
+
{% block content %}
|
|
7
|
+
<h5 class="bold mb-2">{% trans "Recipe Ratings" %}</h5>
|
|
8
|
+
|
|
9
|
+
<table class="border striped no-space">
|
|
10
|
+
<thead>
|
|
11
|
+
<tr>
|
|
12
|
+
<th>{% trans "Recipe" %}</th>
|
|
13
|
+
<th>{% trans "User" %}</th>
|
|
14
|
+
<th class="min center-align">{% trans "Score" %}</th>
|
|
15
|
+
<th>{% trans "Updated" %}</th>
|
|
16
|
+
<th class="right-align">{% trans "Actions" %}</th>
|
|
17
|
+
</tr>
|
|
18
|
+
</thead>
|
|
19
|
+
<tbody>
|
|
20
|
+
{% for r in ratings %}
|
|
21
|
+
<tr>
|
|
22
|
+
<td><b>{{ r.recipe.title }}</b></td>
|
|
23
|
+
<td>{{ r.user.username }}</td>
|
|
24
|
+
<td class="min center-align">
|
|
25
|
+
<div class="row align-center">
|
|
26
|
+
<i class="primary-text">star</i>
|
|
27
|
+
<span>{{ r.score }}</span>
|
|
28
|
+
</div>
|
|
29
|
+
</td>
|
|
30
|
+
<td>{{ r.updated_at|date:"SHORT_DATETIME_FORMAT" }}</td>
|
|
31
|
+
<td class="right-align">
|
|
32
|
+
<a href="{% url 'admin_rating_delete' r.pk %}" class="button circle transparent" title="{% trans 'Delete' %}"><i>delete</i></a>
|
|
33
|
+
</td>
|
|
34
|
+
</tr>
|
|
35
|
+
{% endfor %}
|
|
36
|
+
</tbody>
|
|
37
|
+
</table>
|
|
38
|
+
{% endblock %}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
{% extends "admin/admin_base.html" %}
|
|
2
|
+
{% load i18n %}
|
|
3
|
+
|
|
4
|
+
{% block admin_title %}{{ title }}{% endblock %}
|
|
5
|
+
|
|
6
|
+
{% block extra_head %}
|
|
7
|
+
{{ block.super }}
|
|
8
|
+
<link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css">
|
|
9
|
+
<script src="https://unpkg.com/easymde/dist/easymde.min.js"></script>
|
|
10
|
+
<style>
|
|
11
|
+
/* Ensure EasyMDE fits well with BeerCSS containers */
|
|
12
|
+
.editor-toolbar {
|
|
13
|
+
background: var(--surface-variant) !important;
|
|
14
|
+
color: var(--on-surface-variant) !important;
|
|
15
|
+
border-color: var(--outline) !important;
|
|
16
|
+
opacity: 1 !important;
|
|
17
|
+
}
|
|
18
|
+
.CodeMirror {
|
|
19
|
+
background: var(--surface) !important;
|
|
20
|
+
color: var(--on-surface) !important;
|
|
21
|
+
border-color: var(--outline) !important;
|
|
22
|
+
}
|
|
23
|
+
.editor-toolbar button {
|
|
24
|
+
color: var(--on-surface-variant) !important;
|
|
25
|
+
}
|
|
26
|
+
.editor-toolbar button.active, .editor-toolbar button:hover {
|
|
27
|
+
background: var(--primary-container) !important;
|
|
28
|
+
color: var(--on-primary-container) !important;
|
|
29
|
+
}
|
|
30
|
+
/* Add spacing between sections */
|
|
31
|
+
.form-section {
|
|
32
|
+
margin-bottom: 2rem;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
35
|
+
{% endblock %}
|
|
36
|
+
|
|
37
|
+
{% block content %}
|
|
38
|
+
<form method="post" enctype="multipart/form-data" id="recipe-form">
|
|
39
|
+
{% csrf_token %}
|
|
40
|
+
{{ form.rotation }}
|
|
41
|
+
<div class="grid">
|
|
42
|
+
<!-- Top Section: Title, Tags, and Image -->
|
|
43
|
+
<div class="s12 m8">
|
|
44
|
+
<article class="round padding border mb-2">
|
|
45
|
+
<div class="field label border round">
|
|
46
|
+
{{ form.title }}
|
|
47
|
+
<label>{{ form.title.label }}</label>
|
|
48
|
+
{% if form.title.errors %}<span class="error">{{ form.title.errors|striptags }}</span>{% endif %}
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div class="field label border round mt-1">
|
|
52
|
+
{{ form.tags_string }}
|
|
53
|
+
<label>{{ form.tags_string.label }}</label>
|
|
54
|
+
{% if form.tags_string.errors %}<span class="error">{{ form.tags_string.errors|striptags }}</span>{% endif %}
|
|
55
|
+
<span class="helper">{% trans "Separate tags with commas. New tags will be created automatically." %}</span>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<div class="field label border round mt-1">
|
|
59
|
+
{{ form.uploaded_by }}
|
|
60
|
+
<label>{{ form.uploaded_by.label }}</label>
|
|
61
|
+
{% if form.uploaded_by.errors %}<span class="error">{{ form.uploaded_by.errors|striptags }}</span>{% endif %}
|
|
62
|
+
</div>
|
|
63
|
+
</article>
|
|
64
|
+
|
|
65
|
+
<!-- Description -->
|
|
66
|
+
<div class="form-section">
|
|
67
|
+
<div class="row align-center mb-1">
|
|
68
|
+
<i class="primary-text">description</i>
|
|
69
|
+
<h6 class="bold ml-1">{% trans "Description" %}</h6>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="card border no-padding">
|
|
72
|
+
{{ form.description }}
|
|
73
|
+
</div>
|
|
74
|
+
{% if form.description.errors %}<span class="error">{{ form.description.errors|striptags }}</span>{% endif %}
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div class="s12 m4">
|
|
79
|
+
<article class="round padding border center-align">
|
|
80
|
+
<h6 class="bold mb-1">{% trans "Recipe Image" %}</h6>
|
|
81
|
+
|
|
82
|
+
<div class="relative mb-1" style="overflow: hidden; min-height: 200px; display: flex; align-items: center; justify-content: center;">
|
|
83
|
+
{% if recipe.image %}
|
|
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
|
+
{% else %}
|
|
86
|
+
<div class="medium-height middle-align center-align gray1 round" id="image-placeholder" style="width: 100%;">
|
|
87
|
+
<i class="extra">image</i>
|
|
88
|
+
</div>
|
|
89
|
+
{% endif %}
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
{% if recipe.image %}
|
|
93
|
+
<div class="row no-space border round mb-1">
|
|
94
|
+
<button type="button" class="button transparent max" onclick="rotatePreview(-90)" title="{% trans 'Rotate 90° CCW' %}">
|
|
95
|
+
<i>rotate_left</i>
|
|
96
|
+
</button>
|
|
97
|
+
<div class="divider vertical"></div>
|
|
98
|
+
<button type="button" class="button transparent max" onclick="rotatePreview(90)" title="{% trans 'Rotate 90° CW' %}">
|
|
99
|
+
<i>rotate_right</i>
|
|
100
|
+
</button>
|
|
101
|
+
</div>
|
|
102
|
+
{% endif %}
|
|
103
|
+
|
|
104
|
+
<div class="field file border round">
|
|
105
|
+
<input type="text" readonly>
|
|
106
|
+
{{ form.image }}
|
|
107
|
+
<label>{{ form.image.label }}</label>
|
|
108
|
+
<i>publish</i>
|
|
109
|
+
</div>
|
|
110
|
+
{% if form.image.errors %}<span class="error">{{ form.image.errors|striptags }}</span>{% endif %}
|
|
111
|
+
</article>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<!-- Ingredients and Instructions (Full Width) -->
|
|
115
|
+
<div class="s12">
|
|
116
|
+
<div class="divider mb-2"></div>
|
|
117
|
+
|
|
118
|
+
<div class="form-section">
|
|
119
|
+
<div class="row align-center mb-1">
|
|
120
|
+
<i class="primary-text">shopping_cart</i>
|
|
121
|
+
<h6 class="bold ml-1">{% trans "Ingredients" %}</h6>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="card border no-padding">
|
|
124
|
+
{{ form.ingredients }}
|
|
125
|
+
</div>
|
|
126
|
+
{% if form.ingredients.errors %}<span class="error">{{ form.ingredients.errors|striptags }}</span>{% endif %}
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<div class="form-section">
|
|
130
|
+
<div class="row align-center mb-1">
|
|
131
|
+
<i class="primary-text">list</i>
|
|
132
|
+
<h6 class="bold ml-1">{% trans "Instructions" %}</h6>
|
|
133
|
+
</div>
|
|
134
|
+
<div class="card border no-padding">
|
|
135
|
+
{{ form.instructions }}
|
|
136
|
+
</div>
|
|
137
|
+
{% if form.instructions.errors %}<span class="error">{{ form.instructions.errors|striptags }}</span>{% endif %}
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<div class="large-space"></div>
|
|
143
|
+
<nav class="right-align padding surface-container border round">
|
|
144
|
+
<a href="{% url 'admin_recipe_list' %}" class="button transparent">{% trans "Cancel" %}</a>
|
|
145
|
+
<button type="submit" class="button primary round">
|
|
146
|
+
<i>save</i>
|
|
147
|
+
<span>{% trans "Save Recipe" %}</span>
|
|
148
|
+
</button>
|
|
149
|
+
</nav>
|
|
150
|
+
</form>
|
|
151
|
+
{% endblock %}
|
|
152
|
+
|
|
153
|
+
{% block admin_scripts %}
|
|
154
|
+
<script>
|
|
155
|
+
let currentRotation = 0;
|
|
156
|
+
|
|
157
|
+
function rotatePreview(angle) {
|
|
158
|
+
currentRotation = (currentRotation + angle) % 360;
|
|
159
|
+
const img = document.getElementById('image-preview');
|
|
160
|
+
if (img) {
|
|
161
|
+
img.style.transform = `rotate(${currentRotation}deg)`;
|
|
162
|
+
document.getElementsByName('rotation')[0].value = currentRotation;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
167
|
+
const fields = ['id_description', 'id_ingredients', 'id_instructions'];
|
|
168
|
+
fields.forEach(id => {
|
|
169
|
+
const el = document.getElementById(id);
|
|
170
|
+
if (el) {
|
|
171
|
+
new EasyMDE({
|
|
172
|
+
element: el,
|
|
173
|
+
spellChecker: false,
|
|
174
|
+
autosave: {
|
|
175
|
+
enabled: false
|
|
176
|
+
},
|
|
177
|
+
status: false,
|
|
178
|
+
minHeight: "200px"
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
</script>
|
|
184
|
+
{% endblock %}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{% extends "admin/admin_base.html" %}
|
|
2
|
+
{% load i18n %}
|
|
3
|
+
|
|
4
|
+
{% block admin_title %}{% trans "Recipes" %}{% endblock %}
|
|
5
|
+
|
|
6
|
+
{% block content %}
|
|
7
|
+
<div class="row align-center mb-2">
|
|
8
|
+
<h5 class="max bold">{% trans "Recipe Management" %}</h5>
|
|
9
|
+
<a href="{% url 'admin_recipe_add' %}" class="button primary round">
|
|
10
|
+
<i>add</i>
|
|
11
|
+
<span>{% trans "Add Recipe" %}</span>
|
|
12
|
+
</a>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<table class="border striped no-space">
|
|
16
|
+
<thead>
|
|
17
|
+
<tr>
|
|
18
|
+
<th class="min">{% trans "ID" %}</th>
|
|
19
|
+
<th class="min">{% trans "Image" %}</th>
|
|
20
|
+
<th class="max">{% trans "Title" %}</th>
|
|
21
|
+
<th class="min">{% trans "Rating" %}</th>
|
|
22
|
+
<th class="m l">{% trans "Tags" %}</th>
|
|
23
|
+
<th>{% trans "Uploader" %}</th>
|
|
24
|
+
<th>{% trans "Created" %}</th>
|
|
25
|
+
<th class="right-align">{% trans "Actions" %}</th>
|
|
26
|
+
</tr>
|
|
27
|
+
</thead>
|
|
28
|
+
<tbody>
|
|
29
|
+
{% for recipe in recipes %}
|
|
30
|
+
<tr class="pointer" onclick="location.href='{% url 'admin_recipe_edit' recipe.pk %}'">
|
|
31
|
+
<td class="min">{{ recipe.id }}</td>
|
|
32
|
+
<td class="min">
|
|
33
|
+
{% if recipe.image %}
|
|
34
|
+
<img src="{{ recipe.image_thumbnail.url }}" class="admin-thumb round">
|
|
35
|
+
{% else %}
|
|
36
|
+
<div class="admin-thumb round gray1 middle-align center-align">
|
|
37
|
+
<i class="extra">restaurant</i>
|
|
38
|
+
</div>
|
|
39
|
+
{% endif %}
|
|
40
|
+
</td>
|
|
41
|
+
<td class="max">
|
|
42
|
+
<b>{{ recipe.title }}</b>
|
|
43
|
+
</td>
|
|
44
|
+
<td class="min center-align">
|
|
45
|
+
<div class="row align-center">
|
|
46
|
+
<i class="primary-text">star</i>
|
|
47
|
+
<span>{{ recipe.avg_rating|default:0|floatformat:1 }}</span>
|
|
48
|
+
</div>
|
|
49
|
+
</td>
|
|
50
|
+
<td class="m l">
|
|
51
|
+
{% for tag in recipe.tags.all %}
|
|
52
|
+
<span class="chip tiny">{{ tag.name }}</span>
|
|
53
|
+
{% endfor %}
|
|
54
|
+
</td>
|
|
55
|
+
<td>{{ recipe.uploaded_by.username|default:"-" }}</td>
|
|
56
|
+
<td>{{ recipe.created_at|date:"SHORT_DATE_FORMAT" }}</td>
|
|
57
|
+
<td class="right-align">
|
|
58
|
+
<a href="{% url 'admin_recipe_delete' recipe.pk %}" class="button circle transparent" onclick="event.stopPropagation();" title="{% trans 'Delete' %}"><i>delete</i></a>
|
|
59
|
+
</td>
|
|
60
|
+
</tr>
|
|
61
|
+
{% endfor %}
|
|
62
|
+
</tbody>
|
|
63
|
+
</table>
|
|
64
|
+
{% endblock %}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{% extends "admin/admin_base.html" %}
|
|
2
|
+
{% load i18n %}
|
|
3
|
+
|
|
4
|
+
{% block admin_title %}{{ title }}{% endblock %}
|
|
5
|
+
|
|
6
|
+
{% block content %}
|
|
7
|
+
<article class="round padding">
|
|
8
|
+
<form method="post">
|
|
9
|
+
{% csrf_token %}
|
|
10
|
+
<div class="grid">
|
|
11
|
+
{% for field in form %}
|
|
12
|
+
<div class="s12">
|
|
13
|
+
<div class="field label border round">
|
|
14
|
+
{{ field }}
|
|
15
|
+
<label>{{ field.label }}</label>
|
|
16
|
+
{% if field.errors %}
|
|
17
|
+
<span class="error">{{ field.errors|striptags }}</span>
|
|
18
|
+
{% endif %}
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
{% endfor %}
|
|
22
|
+
</div>
|
|
23
|
+
<div class="space"></div>
|
|
24
|
+
<nav class="right-align">
|
|
25
|
+
<a href="{% url 'admin_tag_list' %}" class="button transparent">{% trans "Cancel" %}</a>
|
|
26
|
+
<button type="submit" class="button primary round">{% trans "Save Tag" %}</button>
|
|
27
|
+
</nav>
|
|
28
|
+
</form>
|
|
29
|
+
</article>
|
|
30
|
+
{% endblock %}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{% extends "admin/admin_base.html" %}
|
|
2
|
+
{% load i18n %}
|
|
3
|
+
|
|
4
|
+
{% block admin_title %}{% trans "Tags" %}{% endblock %}
|
|
5
|
+
|
|
6
|
+
{% block content %}
|
|
7
|
+
<div class="row align-center mb-2">
|
|
8
|
+
<h5 class="max bold">{% trans "Tag Management" %}</h5>
|
|
9
|
+
<a href="{% url 'admin_tag_add' %}" class="button primary round">
|
|
10
|
+
<i>add</i>
|
|
11
|
+
<span>{% trans "Add Tag" %}</span>
|
|
12
|
+
</a>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<table class="border striped no-space">
|
|
16
|
+
<thead>
|
|
17
|
+
<tr>
|
|
18
|
+
<th class="min">{% trans "ID" %}</th>
|
|
19
|
+
<th class="max">{% trans "Name" %}</th>
|
|
20
|
+
<th>{% trans "Slug" %}</th>
|
|
21
|
+
<th class="right-align">{% trans "Actions" %}</th>
|
|
22
|
+
</tr>
|
|
23
|
+
</thead>
|
|
24
|
+
<tbody>
|
|
25
|
+
{% for tag in tags %}
|
|
26
|
+
<tr class="pointer" onclick="location.href='{% url 'admin_tag_edit' tag.pk %}'">
|
|
27
|
+
<td class="min">{{ tag.id }}</td>
|
|
28
|
+
<td class="max"><b>{{ tag.name }}</b></td>
|
|
29
|
+
<td>{{ tag.slug }}</td>
|
|
30
|
+
<td class="right-align">
|
|
31
|
+
<a href="{% url 'admin_tag_delete' tag.pk %}" class="button circle transparent" onclick="event.stopPropagation();" title="{% trans 'Delete' %}"><i>delete</i></a>
|
|
32
|
+
</td>
|
|
33
|
+
</tr>
|
|
34
|
+
{% endfor %}
|
|
35
|
+
</tbody>
|
|
36
|
+
</table>
|
|
37
|
+
{% endblock %}
|