fix: version badge rendering, Jinja2 scoping, navbar i18n, missing GET task endpoint

- Fix version badge showing [object Object] or Python dict dump in
  select_recipe, task_list, and task_complete templates by accessing
  current_version.version_number instead of the whole object
- Fix recipe_editor.html Internal Server Error caused by Jinja2 block
  scoping: {% set %} variables from block content were invisible in
  block extra_js, replaced with direct recipe.* references
- Fix task_editor.html SyntaxError from Italian apostrophe in
  nell'eliminazione breaking JS string literals, switched to |tojson
- Add i18n {{ _() }} wrappers to all hardcoded navbar strings (desktop
  and mobile menus) so language toggle works correctly
- Add app title text "TieMeasureFlow" next to logo in navbar
- Add missing GET /api/tasks/{task_id} endpoint on server that caused
  405 Method Not Allowed when starting measurements

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Adriano
2026-02-07 20:44:33 +01:00
parent 2453e552fb
commit d262ef68af
7 changed files with 40 additions and 26 deletions
+17 -14
View File
@@ -15,6 +15,9 @@
alt="TieMeasureFlow" alt="TieMeasureFlow"
class="h-8 w-auto"> class="h-8 w-auto">
{% endif %} {% endif %}
<span class="text-lg font-bold text-[var(--text-primary)] tracking-tight hidden sm:inline">
TieMeasureFlow
</span>
</a> </a>
</div> </div>
@@ -35,7 +38,7 @@
<svg class="w-4.5 h-4.5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24"> <svg class="w-4.5 h-4.5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"/> <path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"/>
</svg> </svg>
<span>Misure</span> <span>{{ _('Misure') }}</span>
</a> </a>
{% endif %} {% endif %}
@@ -52,7 +55,7 @@
<svg class="w-4.5 h-4.5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24"> <svg class="w-4.5 h-4.5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/> <path stroke-linecap="round" stroke-linejoin="round" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg> </svg>
<span>Ricette</span> <span>{{ _('Ricette') }}</span>
</a> </a>
{% endif %} {% endif %}
@@ -69,7 +72,7 @@
<svg class="w-4.5 h-4.5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24"> <svg class="w-4.5 h-4.5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/> <path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
</svg> </svg>
<span>Statistiche</span> <span>{{ _('Statistiche') }}</span>
</a> </a>
{% endif %} {% endif %}
@@ -86,7 +89,7 @@
<path stroke-linecap="round" stroke-linejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/> <path stroke-linecap="round" stroke-linejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/> <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg> </svg>
<span>Admin</span> <span>{{ _('Admin') }}</span>
<svg class="w-3.5 h-3.5 transition-transform duration-200" :class="{ 'rotate-180': open }" <svg class="w-3.5 h-3.5 transition-transform duration-200" :class="{ 'rotate-180': open }"
fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"/> <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"/>
@@ -108,14 +111,14 @@
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/> <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/>
</svg> </svg>
Utenti {{ _('Utenti') }}
</a> </a>
<a href="#" class="flex items-center gap-2.5 px-4 py-2.5 text-sm text-[var(--text-secondary)] <a href="#" class="flex items-center gap-2.5 px-4 py-2.5 text-sm text-[var(--text-secondary)]
hover:text-primary hover:bg-primary-50 dark:hover:bg-primary-900/20 transition-colors"> hover:text-primary hover:bg-primary-50 dark:hover:bg-primary-900/20 transition-colors">
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/> <path stroke-linecap="round" stroke-linejoin="round" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/>
</svg> </svg>
Impostazioni {{ _('Impostazioni') }}
</a> </a>
</div> </div>
</div> </div>
@@ -211,7 +214,7 @@
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/> <path stroke-linecap="round" stroke-linejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/>
</svg> </svg>
Profilo {{ _('Profilo') }}
</a> </a>
<div class="border-t border-[var(--border-color)] my-1"></div> <div class="border-t border-[var(--border-color)] my-1"></div>
@@ -222,7 +225,7 @@
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/> <path stroke-linecap="round" stroke-linejoin="round" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/>
</svg> </svg>
Logout {{ _('Logout') }}
</a> </a>
</div> </div>
</div> </div>
@@ -267,7 +270,7 @@
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24"> <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"/> <path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"/>
</svg> </svg>
Misure {{ _('Misure') }}
</a> </a>
{% endif %} {% endif %}
@@ -279,7 +282,7 @@
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24"> <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/> <path stroke-linecap="round" stroke-linejoin="round" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg> </svg>
Ricette {{ _('Ricette') }}
</a> </a>
{% endif %} {% endif %}
@@ -291,13 +294,13 @@
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24"> <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/> <path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
</svg> </svg>
Statistiche {{ _('Statistiche') }}
</a> </a>
{% endif %} {% endif %}
{% if current_user.get('is_admin') %} {% if current_user.get('is_admin') %}
<div class="border-t border-[var(--border-color)] my-2"></div> <div class="border-t border-[var(--border-color)] my-2"></div>
<p class="px-3 py-1 text-xs font-semibold text-[var(--text-secondary)] uppercase tracking-wider">Admin</p> <p class="px-3 py-1 text-xs font-semibold text-[var(--text-secondary)] uppercase tracking-wider">{{ _('Admin') }}</p>
<a href="#" <a href="#"
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium
text-[var(--text-secondary)] hover:text-primary hover:bg-primary-50 dark:hover:bg-primary-900/20 text-[var(--text-secondary)] hover:text-primary hover:bg-primary-50 dark:hover:bg-primary-900/20
@@ -305,7 +308,7 @@
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24"> <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/> <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/>
</svg> </svg>
Utenti {{ _('Utenti') }}
</a> </a>
<a href="#" <a href="#"
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium
@@ -314,7 +317,7 @@
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24"> <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/> <path stroke-linecap="round" stroke-linejoin="round" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/>
</svg> </svg>
Impostazioni {{ _('Impostazioni') }}
</a> </a>
{% endif %} {% endif %}
+6 -6
View File
@@ -585,8 +585,8 @@
function recipeEditor() { function recipeEditor() {
return { return {
// ---- Mode ---- // ---- Mode ----
isNew: {{ 'true' if is_new else 'false' }}, isNew: {{ 'true' if recipe is none else 'false' }},
recipeId: {{ recipe_id|tojson if recipe_id else 'null' }}, recipeId: {{ recipe.id|tojson if recipe else 'null' }},
// ---- Form data ---- // ---- Form data ----
code: {{ (recipe.code if recipe else '')|tojson }}, code: {{ (recipe.code if recipe else '')|tojson }},
@@ -595,12 +595,12 @@ function recipeEditor() {
changeNotes: '', changeNotes: '',
// ---- File upload ---- // ---- File upload ----
currentFilePath: {{ (current_version.tasks[0].file_path if current_version and current_version.tasks and current_version.tasks|length > 0 else None)|tojson }}, currentFilePath: {{ (recipe.current_version.tasks[0].file_path if recipe and recipe.current_version and recipe.current_version.tasks and recipe.current_version.tasks|length > 0 else None)|tojson }},
uploadingFile: false, uploadingFile: false,
dragOver: false, dragOver: false,
// ---- Annotation state (managed by annotation-editor.js) ---- // ---- Annotation state (managed by annotation-editor.js) ----
annotationsJson: {{ (current_version.tasks[0].annotations_json if current_version and current_version.tasks and current_version.tasks|length > 0 and current_version.tasks[0].annotations_json else None)|tojson }}, annotationsJson: {{ (recipe.current_version.tasks[0].annotations_json if recipe and recipe.current_version and recipe.current_version.tasks and recipe.current_version.tasks|length > 0 and recipe.current_version.tasks[0].annotations_json else None)|tojson }},
annoTool: 'select', annoTool: 'select',
annoSelected: false, annoSelected: false,
@@ -610,8 +610,8 @@ function recipeEditor() {
errorMessage: '', errorMessage: '',
// ---- Versioning ---- // ---- Versioning ----
versions: {{ versions|tojson }}, versions: {{ (recipe.versions if recipe and recipe.versions else [])|tojson }},
currentVersion: {{ (current_version.version_number if current_version else 0)|tojson }}, currentVersion: {{ (recipe.current_version.version_number if recipe and recipe.current_version else 0)|tojson }},
// ============================================================ // ============================================================
// Init // Init
+2 -2
View File
@@ -1227,7 +1227,7 @@ function taskEditor() {
this.successMessage = '{{ _("Task eliminato") }}'; this.successMessage = '{{ _("Task eliminato") }}';
} else { } else {
const data = await resp.json(); const data = await resp.json();
this.errorMessage = data.detail || '{{ _("Errore nell\'eliminazione del task") }}'; this.errorMessage = data.detail || {{ _("Errore nell'eliminazione del task")|tojson }};
} }
} catch (err) { } catch (err) {
console.error('deleteTask error:', err); console.error('deleteTask error:', err);
@@ -1510,7 +1510,7 @@ function taskEditor() {
this.successMessage = '{{ _("Misurazione eliminata") }}'; this.successMessage = '{{ _("Misurazione eliminata") }}';
} else { } else {
const data = await resp.json(); const data = await resp.json();
this.errorMessage = data.detail || '{{ _("Errore nell\'eliminazione della misurazione") }}'; this.errorMessage = data.detail || {{ _("Errore nell'eliminazione della misurazione")|tojson }};
} }
} catch (err) { } catch (err) {
console.error('deleteSubtask error:', err); console.error('deleteSubtask error:', err);
+1 -1
View File
@@ -150,7 +150,7 @@
</span> </span>
<span class="badge badge-neutral text-xs" <span class="badge badge-neutral text-xs"
x-show="recipe.current_version || recipe.version" x-show="recipe.current_version || recipe.version"
x-text="'v' + (recipe.current_version || recipe.version || '')"> x-text="'v' + (recipe.current_version ? recipe.current_version.version_number : (recipe.version || ''))">
</span> </span>
</div> </div>
+2 -2
View File
@@ -55,7 +55,7 @@
</div> </div>
<div> <div>
<p class="text-sm font-medium mb-1" style="color: var(--text-muted);">{{ _('Versione') }}</p> <p class="text-sm font-medium mb-1" style="color: var(--text-muted);">{{ _('Versione') }}</p>
<p class="font-mono font-medium" style="color: var(--text-primary);">{{ recipe.current_version or recipe.version }}</p> <p class="font-mono font-medium" style="color: var(--text-primary);">{{ recipe.current_version.version_number if recipe.current_version else recipe.version }}</p>
</div> </div>
<div> <div>
<p class="text-sm font-medium mb-1" style="color: var(--text-muted);">{{ _('Lotto') }}</p> <p class="text-sm font-medium mb-1" style="color: var(--text-muted);">{{ _('Lotto') }}</p>
@@ -299,7 +299,7 @@ window.CSV_I18N = {
window.measurementData = { window.measurementData = {
recipeName: {{ recipe.name|tojson }}, recipeName: {{ recipe.name|tojson }},
recipeCode: {{ recipe.code|tojson }}, recipeCode: {{ recipe.code|tojson }},
version: {{ (recipe.current_version or recipe.version)|tojson }}, version: {{ (recipe.current_version.version_number if recipe.current_version else recipe.version)|tojson }},
lotNumber: {{ (lot_number or '')|tojson }}, lotNumber: {{ (lot_number or '')|tojson }},
serialNumber: {{ (serial_number or '')|tojson }}, serialNumber: {{ (serial_number or '')|tojson }},
measurements: {{ measurements|tojson }} measurements: {{ measurements|tojson }}
+1 -1
View File
@@ -44,7 +44,7 @@
<!-- Version Badge --> <!-- Version Badge -->
{% if recipe.current_version or recipe.version %} {% if recipe.current_version or recipe.version %}
<span class="badge badge-neutral"> <span class="badge badge-neutral">
v{{ recipe.current_version or recipe.version }} v{{ recipe.current_version.version_number if recipe.current_version else recipe.version }}
</span> </span>
{% endif %} {% endif %}
</div> </div>
+11
View File
@@ -112,6 +112,17 @@ async def list_tasks(
return [TaskResponse.model_validate(t) for t in tasks] return [TaskResponse.model_validate(t) for t in tasks]
@router.get("/api/tasks/{task_id}", response_model=TaskResponse)
async def get_task(
task_id: int,
_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""Get a single task with its subtasks. All authenticated users."""
task = await _get_task_or_404(db, task_id)
return TaskResponse.model_validate(task)
@router.post( @router.post(
"/api/recipes/{recipe_id}/tasks", "/api/recipes/{recipe_id}/tasks",
response_model=TaskResponse, response_model=TaskResponse,