sandwitches 1.4.2__py3-none-any.whl → 1.5.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.
Files changed (67) hide show
  1. sandwitches/__init__.py +6 -0
  2. sandwitches/admin.py +21 -2
  3. sandwitches/api.py +112 -6
  4. sandwitches/feeds.py +23 -0
  5. sandwitches/forms.py +110 -7
  6. sandwitches/locale/nl/LC_MESSAGES/django.mo +0 -0
  7. sandwitches/locale/nl/LC_MESSAGES/django.po +784 -134
  8. sandwitches/migrations/0001_initial.py +255 -2
  9. sandwitches/migrations/0002_historicalrecipe_servings_recipe_servings.py +27 -0
  10. sandwitches/migrations/0003_setting.py +35 -0
  11. sandwitches/migrations/0004_alter_setting_ai_api_key_and_more.py +37 -0
  12. sandwitches/migrations/0005_rating_comment.py +17 -0
  13. sandwitches/models.py +48 -4
  14. sandwitches/settings.py +14 -5
  15. sandwitches/storage.py +44 -12
  16. sandwitches/templates/admin/admin_base.html +118 -0
  17. sandwitches/templates/admin/confirm_delete.html +23 -0
  18. sandwitches/templates/admin/dashboard.html +262 -0
  19. sandwitches/templates/admin/rating_list.html +38 -0
  20. sandwitches/templates/admin/recipe_form.html +184 -0
  21. sandwitches/templates/admin/recipe_list.html +64 -0
  22. sandwitches/templates/admin/tag_form.html +30 -0
  23. sandwitches/templates/admin/tag_list.html +37 -0
  24. sandwitches/templates/admin/task_detail.html +91 -0
  25. sandwitches/templates/admin/task_list.html +41 -0
  26. sandwitches/templates/admin/user_form.html +37 -0
  27. sandwitches/templates/admin/user_list.html +60 -0
  28. sandwitches/templates/base.html +80 -1
  29. sandwitches/templates/base_beer.html +57 -0
  30. sandwitches/templates/components/favorites_search_form.html +85 -0
  31. sandwitches/templates/components/footer.html +14 -0
  32. sandwitches/templates/components/ingredients_scripts.html +50 -0
  33. sandwitches/templates/components/ingredients_section.html +11 -0
  34. sandwitches/templates/components/instructions_section.html +9 -0
  35. sandwitches/templates/components/language_dialog.html +26 -0
  36. sandwitches/templates/components/navbar.html +27 -0
  37. sandwitches/templates/components/rating_section.html +66 -0
  38. sandwitches/templates/components/recipe_header.html +32 -0
  39. sandwitches/templates/components/search_form.html +106 -0
  40. sandwitches/templates/components/search_scripts.html +98 -0
  41. sandwitches/templates/components/side_menu.html +31 -0
  42. sandwitches/templates/components/user_menu.html +10 -0
  43. sandwitches/templates/detail.html +167 -110
  44. sandwitches/templates/favorites.html +42 -0
  45. sandwitches/templates/index.html +28 -61
  46. sandwitches/templates/partials/recipe_list.html +87 -0
  47. sandwitches/templates/recipe_form.html +119 -0
  48. sandwitches/templates/setup.html +1 -1
  49. sandwitches/templates/signup.html +114 -31
  50. sandwitches/templatetags/custom_filters.py +15 -0
  51. sandwitches/urls.py +56 -0
  52. sandwitches/utils.py +222 -0
  53. sandwitches/views.py +503 -14
  54. sandwitches-1.5.0.dist-info/METADATA +104 -0
  55. sandwitches-1.5.0.dist-info/RECORD +62 -0
  56. sandwitches/migrations/0002_historicalrecipe.py +0 -61
  57. sandwitches/migrations/0003_rating.py +0 -57
  58. sandwitches/migrations/0004_add_uploaded_by.py +0 -25
  59. sandwitches/migrations/0005_historicalrecipe_uploaded_by.py +0 -27
  60. sandwitches/migrations/0006_profile.py +0 -48
  61. sandwitches/migrations/0007_alter_rating_score.py +0 -23
  62. sandwitches/migrations/0008_delete_profile.py +0 -15
  63. sandwitches/templates/base_pico.html +0 -260
  64. sandwitches/templates/form.html +0 -16
  65. sandwitches-1.4.2.dist-info/METADATA +0 -25
  66. sandwitches-1.4.2.dist-info/RECORD +0 -35
  67. {sandwitches-1.4.2.dist-info → sandwitches-1.5.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,50 @@
1
+ {% load i18n static %}
2
+
3
+ <script>
4
+ async function scaleIngredients() {
5
+ const portionsInput = document.getElementById('portions');
6
+ const ingredientsDisplay = document.getElementById('ingredients-display');
7
+ const selector = document.querySelector('.portion-selector');
8
+
9
+ if (!portionsInput || !ingredientsDisplay || !selector) return;
10
+
11
+ const recipeId = selector.dataset.recipeId;
12
+ const targetServings = portionsInput.value;
13
+
14
+ ingredientsDisplay.innerHTML = '<div class="center-align padding"><progress class="circle"></progress></div>';
15
+
16
+ try {
17
+ const url = `/api/v1/recipes/${recipeId}/scale-ingredients?target_servings=${targetServings}`;
18
+ console.log("Fetching:", url);
19
+ const response = await fetch(url);
20
+
21
+ if (!response.ok) throw new Error(`Status: ${response.status}`);
22
+
23
+ const data = await response.json();
24
+
25
+ let html = '<div class="padding">';
26
+ data.forEach(item => {
27
+ html += `<div class="mb-1">${item.scaled_line}</div>`;
28
+ });
29
+ html += '</div>';
30
+ ingredientsDisplay.innerHTML = html;
31
+ } catch (e) {
32
+ console.error("Scaling failed:", e);
33
+ ingredientsDisplay.innerHTML = `<div class="padding error-text">{% trans "Error scaling ingredients:" %} ${e.message}</div>`;
34
+ }
35
+ }
36
+
37
+ function adjustPortions(delta) {
38
+ const input = document.getElementById('portions');
39
+ if (!input) return;
40
+ let val = parseInt(input.value) || 1;
41
+ val += delta;
42
+ if (val < 1) val = 1;
43
+ input.value = val;
44
+ scaleIngredients();
45
+ }
46
+
47
+ document.addEventListener('DOMContentLoaded', () => {
48
+ scaleIngredients();
49
+ });
50
+ </script>
@@ -0,0 +1,11 @@
1
+ {% load i18n %}
2
+ <h5 class="primary-text">{% trans "Ingredients" %}</h5>
3
+ <div class="portion-selector" data-recipe-id="{{ recipe.id }}" data-original-servings="{{ recipe.servings }}">
4
+ <label for="portions">{% trans "Portions" %}:</label>
5
+ <button class="button circle transparent" onclick="adjustPortions(-1)">-</button>
6
+ <input type="number" id="portions" value="{{ recipe.servings }}" min="1" onchange="scaleIngredients()">
7
+ <button class="button circle transparent" onclick="adjustPortions(1)">+</button>
8
+ </div>
9
+ <div id="ingredients-display" class="card padding flat gray1">
10
+ <p class="text-center">{% trans "Loading ingredients..." %}</p>
11
+ </div>
@@ -0,0 +1,9 @@
1
+ {% load i18n markdown_extras %}
2
+ <h5 class="primary-text">{% trans "Instructions" %}</h5>
3
+ <div>
4
+ {% if recipe.instructions %}
5
+ {{ recipe.instructions|convert_markdown|safe }}
6
+ {% else %}
7
+ <p class="italic">{% trans "No instructions yet." %}</p>
8
+ {% endif %}
9
+ </div>
@@ -0,0 +1,26 @@
1
+ {% load i18n %}
2
+ <dialog id="language-menu">
3
+ <form method="post" action="{% url 'set_language' %}">
4
+ {% csrf_token %}
5
+ <h5 class="center-align">{% trans "Select Language" %}</h5>
6
+ <div class="grid center-align">
7
+ {% get_current_language as LANGUAGE_CODE %}
8
+ {% get_available_languages as LANGUAGES %}
9
+ {% for code, name in LANGUAGES %}
10
+ <div class="s6">
11
+ <button type="submit" name="language" value="{{ code }}" class="circle large {% if code == LANGUAGE_CODE %}primary{% else %}surface{% endif %}">
12
+ <span style="font-size: 2rem;">
13
+ {% if code == 'en' %}🇬🇧{% elif code == 'nl' %}🇳🇱{% else %}🌐{% endif %}
14
+ </span>
15
+ </button>
16
+ <div class="small-text">{{ name }}</div>
17
+ </div>
18
+ {% endfor %}
19
+ </div>
20
+ <input type="hidden" name="next" value="{{ request.get_full_path }}" />
21
+ </form>
22
+ <div class="space"></div>
23
+ <nav class="center-align">
24
+ <button class="transparent link" data-ui="#language-menu">{% trans "Close" %}</button>
25
+ </nav>
26
+ </dialog>
@@ -0,0 +1,27 @@
1
+ {% load i18n static %}
2
+ <nav class="top primary-container">
3
+ <button class="circle transparent large" onclick="ui('#menu')">
4
+ <i>menu</i>
5
+ </button>
6
+ <a href="{% url 'index' %}" class="row align-center">
7
+ <img src="{% static 'icons/icon.svg' %}" class="circle small">
8
+ </a>
9
+ <div class="max"></div>
10
+ <button class="circle transparent" onclick="ui('mode', ui('mode') == 'dark' ? 'light' : 'dark')">
11
+ <i>dark_mode</i>
12
+ </button>
13
+ <button class="circle transparent" data-ui="#language-menu">
14
+ <i>language</i>
15
+ </button>
16
+
17
+ {% if user.is_authenticated %}
18
+ {% if user.avatar %}
19
+ <img src="{{ user.avatar.url }}" class="circle" data-ui="#user-menu">
20
+ {% else %}
21
+ <img src="https://www.w3schools.com/howto/img_avatar.png" class="circle" data-ui="#user-menu">
22
+ {% endif %}
23
+ {% else %}
24
+ <a href="{% url 'admin:login' %}"><button class="chip transparent border white-text">{% trans "Login" %}</button></a>
25
+ <a href="{% url 'signup' %}"><button class="chip primary">{% trans "Sign up" %}</button></a>
26
+ {% endif %}
27
+ </nav>
@@ -0,0 +1,66 @@
1
+ {% load i18n %}
2
+ <h6 class="bold">{% trans "Rating" %}</h6>
3
+ <div class="row align-center">
4
+ <i class="primary-text">star</i>
5
+ <span class="h5 ml-1">{{ avg_rating|floatformat:1 }}</span>
6
+ <span class="small-text ml-1">({{ rating_count }} {% trans "vote" %}{% if rating_count != 1 %}s{% endif %})</span>
7
+ </div>
8
+
9
+ <div class="space"></div>
10
+
11
+ {% if user.is_authenticated %}
12
+ <article class="border round no-elevate secondary-container">
13
+ {% if user_rating %}
14
+ <div class="row align-center">
15
+ <i class="primary-text">check_circle</i>
16
+ <span class="ml-2">{% trans "Your rating:" %} <b>{{ user_rating.score }}</b></span>
17
+ </div>
18
+ {% if user_rating.comment %}
19
+ <p class="small-text ml-2">{{ user_rating.comment }}</p>
20
+ {% endif %}
21
+ {% endif %}
22
+
23
+ <form method="post" action="{% url 'recipe_rate' pk=recipe.pk %}">
24
+ {% csrf_token %}
25
+ <label class="bold">{% trans "Rate this sandwich" %}</label>
26
+ <div class="field middle-align">
27
+ <label class="slider">
28
+ <input type="range"
29
+ name="{{ rating_form.score.name }}"
30
+ min="0"
31
+ max="10"
32
+ step="0.1"
33
+ value="{{ user_rating.score|default:'5.0' }}"
34
+ oninput="document.getElementById('score-output').innerText = parseFloat(this.value).toFixed(1)">
35
+ <span></span>
36
+ </label>
37
+ <span id="score-output" class="bold ml-2">{{ user_rating.score|default:"5.0" }}</span>
38
+ </div>
39
+ <div class="field textarea label border round mt-2">
40
+ <textarea name="{{ rating_form.comment.name }}" placeholder="{% trans "Add a comment (optional)" %}">{{ user_rating.comment|default:"" }}</textarea>
41
+ <label>{% trans "Comment" %}</label>
42
+ </div>
43
+ <button type="submit" class="button primary round mt-2">{% trans "Submit Rating" %}</button>
44
+ </form>
45
+ </article>
46
+ {% else %}
47
+ <p><a href="{% url 'admin:login' %}" class="link">{% trans "Log in" %}</a> {% trans "to rate this recipe." %}</p>
48
+ {% endif %}
49
+
50
+ {% if all_ratings %}
51
+ <div class="space"></div>
52
+ <h6 class="bold">{% trans "All Ratings" %}</h6>
53
+ <div class="collection">
54
+ {% for rating in all_ratings %}
55
+ <div class="row">
56
+ <div class="max">
57
+ <span class="bold">{{ rating.user.username }}</span>
58
+ <span class="ml-2">{{ rating.score|floatformat:1 }} <i class="small">star</i></span>
59
+ </div>
60
+ {% if rating.comment %}
61
+ <p class="small-text">{{ rating.comment }}</p>
62
+ {% endif %}
63
+ </div>
64
+ {% endfor %}
65
+ </div>
66
+ {% endif %}
@@ -0,0 +1,32 @@
1
+ {% load i18n %}
2
+ <div class="row align-center">
3
+ <h4 class="bold max">{{ recipe.title }}</h4>
4
+ {% if user.is_authenticated %}
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 %}">
7
+ {% if recipe in user.favorites.all %}favorite{% else %}favorite_border{% endif %}
8
+ </i>
9
+ </a>
10
+ {% endif %}
11
+ </div>
12
+
13
+ <div class="row align-center">
14
+ {% if recipe.uploaded_by %}
15
+ <a href="{% url 'index' %}?uploader={{ recipe.uploaded_by.username }}" class="row align-center" style="color: inherit; text-decoration: none;">
16
+ {% if recipe.uploaded_by.avatar %}
17
+ <img src="{{ recipe.uploaded_by.avatar.url }}" class="circle small" alt="{{ recipe.uploaded_by.username }}">
18
+ {% else %}
19
+ <i class="small circle surface">person</i>
20
+ {% endif %}
21
+ <div class="ml-2">
22
+ <span class="small-text">{% trans "Uploaded by" %}</span>
23
+ <div class="bold">{{ recipe.uploaded_by.get_full_name|default:recipe.uploaded_by.username }}</div>
24
+ </div>
25
+ </a>
26
+ {% else %}
27
+ <i class="small circle surface">person_off</i>
28
+ <div class="ml-2">
29
+ <span class="small-text">{% trans "Unknown uploader" %}</span>
30
+ </div>
31
+ {% endif %}
32
+ </div>
@@ -0,0 +1,106 @@
1
+ {% load i18n %}
2
+ <form hx-get="{% url 'index' %}" hx-target="#recipe-grid" hx-swap="outerHTML" hx-push-url="true" hx-trigger="change delay:500ms, keyup delay:500ms from:#search, submit, uploaderChange from:body">
3
+ <div class="grid">
4
+ <!-- Main Search Bar -->
5
+ <div class="s12">
6
+ <div class="field prefix suffix border round primary-container">
7
+ <i class="front">search</i>
8
+ <input id="search" type="text" name="q" placeholder="{% trans 'Search by title...' %}" value="{{ request.GET.q|default:'' }}">
9
+ <a class="circle plain" onclick="toggleAdvanced()" title="{% trans 'Advanced Search' %}">
10
+ <i id="advanced-icon">filter_list</i>
11
+ </a>
12
+ </div>
13
+ </div>
14
+
15
+ <!-- Collapsible Advanced Search Panel -->
16
+ <div id="advanced-search" class="s12 hidden" style="display: none;">
17
+ <div class="padding surface border round">
18
+ <div class="grid">
19
+ <div class="s12 m6 l3">
20
+ <div class="field label border round">
21
+ <input type="date" name="date_start" value="{{ request.GET.date_start|default:'' }}">
22
+ <label>{% trans "Start Date" %}</label>
23
+ </div>
24
+ </div>
25
+ <div class="s12 m6 l3">
26
+ <div class="field label border round">
27
+ <input type="date" name="date_end" value="{{ request.GET.date_end|default:'' }}">
28
+ <label>{% trans "End Date" %}</label>
29
+ </div>
30
+ </div>
31
+
32
+ <!-- Custom Uploader Dropdown with Avatar -->
33
+ <div class="s12 m6 l3">
34
+ <div class="field label border round suffix relative" onclick="toggleUploaderMenu(event)">
35
+ <input type="text" id="uploader-display" readonly value="{{ request.GET.uploader|default:'' }}" placeholder="{% trans 'All Uploaders' %}" style="cursor: pointer;">
36
+ <label>{% trans "Uploader" %}</label>
37
+ <i class="chevron-down">arrow_drop_down</i>
38
+
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
+ <a class="row padding hover pointer" onclick="selectUploader('', '{% trans 'All Uploaders' %}')">
41
+ <span>{% trans "All Uploaders" %}</span>
42
+ </a>
43
+ {% for u in uploaders %}
44
+ <a class="row padding hover pointer align-center" onclick="selectUploader('{{ u.username }}', '{{ u.username }}')">
45
+ {% if u.avatar %}
46
+ <img src="{{ u.avatar.url }}" class="circle tiny margin-right">
47
+ {% else %}
48
+ <i class="tiny circle surface margin-right">person</i>
49
+ {% endif %}
50
+ <span>{{ u.username }}</span>
51
+ </a>
52
+ {% endfor %}
53
+ </div>
54
+ </div>
55
+ <input type="hidden" name="uploader" id="uploader-hidden" value="{{ request.GET.uploader|default:'' }}">
56
+ </div>
57
+
58
+ <div class="s12 m6 l3">
59
+ <div class="field label border round suffix relative" onclick="toggleTagMenu(event)">
60
+ <input type="text" id="tag-display" readonly placeholder="{% trans 'All Tags' %}" style="cursor: pointer;">
61
+ <label>{% trans "Tags" %}</label>
62
+ <i class="chevron-down">arrow_drop_down</i>
63
+
64
+ <div id="tag-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;" onclick="event.stopPropagation()">
65
+ {% for t in tags %}
66
+ <label class="row padding hover pointer align-center">
67
+ <input type="checkbox" class="margin-right" onchange="updateSelectedTags()" value="{{ t.name }}" {% if t.name in selected_tags %}checked{% endif %}>
68
+ <span>{{ t.name }}</span>
69
+ </label>
70
+ {% endfor %}
71
+ </div>
72
+ </div>
73
+ <div id="tag-hidden-container">
74
+ {% for t in selected_tags %}
75
+ <input type="hidden" name="tag" value="{{ t }}">
76
+ {% endfor %}
77
+ </div>
78
+ </div>
79
+
80
+ {% if user.is_authenticated %}
81
+ <div class="s12 m6 l3">
82
+ <div class="field middle-align">
83
+ <label class="checkbox">
84
+ <input type="checkbox" name="favorites" {% if request.GET.favorites == 'on' %}checked{% endif %}>
85
+ <span>{% trans "My Favorites" %}</span>
86
+ </label>
87
+ </div>
88
+ </div>
89
+ {% endif %}
90
+
91
+ <div class="s12 m6 l3">
92
+ <div class="field label border round">
93
+ <select name="sort">
94
+ <option value="date_desc" {% if request.GET.sort == 'date_desc' %}selected{% endif %}>{% trans "Newest" %}</option>
95
+ <option value="date_asc" {% if request.GET.sort == 'date_asc' %}selected{% endif %}>{% trans "Oldest" %}</option>
96
+ <option value="rating" {% if request.GET.sort == 'rating' %}selected{% endif %}>{% trans "Highest Rated" %}</option>
97
+ <option value="user" {% if request.GET.sort == 'user' %}selected{% endif %}>{% trans "Uploader (A-Z)" %}</option>
98
+ </select>
99
+ <label>{% trans "Sort By" %}</label>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ </form>
@@ -0,0 +1,98 @@
1
+ <script>
2
+ function toggleAdvanced() {
3
+ var el = document.getElementById('advanced-search');
4
+ if (el.style.display === 'none') {
5
+ el.style.display = 'block';
6
+ } else {
7
+ el.style.display = 'none';
8
+ }
9
+ }
10
+
11
+ function toggleUploaderMenu(event) {
12
+ event.stopPropagation(); // Prevent closing immediately
13
+ var el = document.getElementById('uploader-menu');
14
+ if (el.style.display === 'none') {
15
+ // Close others if needed, but for now just toggle
16
+ el.style.display = 'block';
17
+ } else {
18
+ el.style.display = 'none';
19
+ }
20
+ }
21
+
22
+ function selectUploader(username, displayName) {
23
+ document.getElementById('uploader-hidden').value = username;
24
+ document.getElementById('uploader-display').value = username ? username : ''; // Or displayName
25
+
26
+ // Trigger HTMX
27
+ document.body.dispatchEvent(new Event('uploaderChange'));
28
+
29
+ // Close menu (handled by toggle or outside click)
30
+ // document.getElementById('uploader-menu').style.display = 'none';
31
+ }
32
+
33
+ function toggleTagMenu(event) {
34
+ event.stopPropagation();
35
+ var el = document.getElementById('tag-menu');
36
+ if (el.style.display === 'none') {
37
+ el.style.display = 'block';
38
+ } else {
39
+ el.style.display = 'none';
40
+ }
41
+ }
42
+
43
+ function updateSelectedTags(triggerEvent = true) {
44
+ var menu = document.getElementById('tag-menu');
45
+ var checkboxes = menu.querySelectorAll('input[type="checkbox"]');
46
+ var hiddenContainer = document.getElementById('tag-hidden-container');
47
+ var displayInput = document.getElementById('tag-display');
48
+
49
+ hiddenContainer.innerHTML = ''; // Clear existing
50
+ var selectedNames = [];
51
+
52
+ checkboxes.forEach(function(cb) {
53
+ if (cb.checked) {
54
+ selectedNames.push(cb.value);
55
+ var input = document.createElement('input');
56
+ input.type = 'hidden';
57
+ input.name = 'tag';
58
+ input.value = cb.value;
59
+ hiddenContainer.appendChild(input);
60
+ }
61
+ });
62
+
63
+ if (selectedNames.length > 0) {
64
+ displayInput.value = selectedNames.join(', ');
65
+ } else {
66
+ displayInput.value = '';
67
+ }
68
+
69
+ if (triggerEvent) {
70
+ // Trigger HTMX
71
+ document.body.dispatchEvent(new Event('uploaderChange'));
72
+ }
73
+ }
74
+
75
+ // Initialize display value
76
+ document.addEventListener('DOMContentLoaded', function() {
77
+ updateSelectedTags(false);
78
+ });
79
+
80
+ // Close menus on clicking outside
81
+ document.addEventListener('click', function(event) {
82
+ var uploaderMenu = document.getElementById('uploader-menu');
83
+ var uploaderField = document.getElementById('uploader-display').parentElement;
84
+ if (uploaderMenu && uploaderMenu.style.display === 'block') {
85
+ if (!uploaderField.contains(event.target)) {
86
+ uploaderMenu.style.display = 'none';
87
+ }
88
+ }
89
+
90
+ var tagMenu = document.getElementById('tag-menu');
91
+ var tagField = document.getElementById('tag-display').parentElement;
92
+ if (tagMenu && tagMenu.style.display === 'block') {
93
+ if (!tagField.contains(event.target)) {
94
+ tagMenu.style.display = 'none';
95
+ }
96
+ }
97
+ });
98
+ </script>
@@ -0,0 +1,31 @@
1
+ {% load i18n static %}
2
+ <dialog id="menu" class="left">
3
+ <nav class="drawer vertical">
4
+ <header>
5
+ <img src="{% static 'icons/icon.svg' %}" class="circle large">
6
+ <h5 class="max">Sandwitches</h5>
7
+ </header>
8
+ <div class="space"></div>
9
+ <a href="{% url 'index' %}" class="padding {% if request.resolver_match.url_name == 'index' %}active{% endif %}">
10
+ <i class="extra padding">home</i>
11
+ <span class="large-text">{% trans "Home" %}</span>
12
+ </a>
13
+ {% if user.is_authenticated %}
14
+ <a href="{% url 'favorites' %}" class="padding {% if request.resolver_match.url_name == 'favorites' %}active{% endif %}">
15
+ <i class="extra padding">favorite</i>
16
+ <span class="large-text">{% trans "Favorites" %}</span>
17
+ </a>
18
+ {% endif %}
19
+ <a href="/api/docs" class="padding">
20
+ <i class="extra padding">api</i>
21
+ <span class="large-text">{% trans "API Docs" %}</span>
22
+ </a>
23
+ {% if user.is_staff %}
24
+ <div class="divider"></div>
25
+ <a href="{% url 'admin_dashboard' %}" class="padding">
26
+ <i class="extra padding">admin_panel_settings</i>
27
+ <span class="large-text">{% trans "Admin Dashboard" %}</span>
28
+ </a>
29
+ {% endif %}
30
+ </nav>
31
+ </dialog>
@@ -0,0 +1,10 @@
1
+ {% load i18n %}
2
+ {% if user.is_authenticated %}
3
+ <menu id="user-menu" class="no-wrap left">
4
+ {% if user.is_staff %}
5
+ <a href="{% url 'admin_dashboard' %}" class="row"><i>admin_panel_settings</i>{% trans "Admin" %}</a>
6
+ <div class="divider"></div>
7
+ {% endif %}
8
+ <a href="{% url 'admin:logout' %}" class="row"><i>logout</i>{% trans "Logout" %}</a>
9
+ </menu>
10
+ {% endif %}