fix: Alpine.js x-data broken by inner double quotes + API list response crash
- Extract JSON data to <script> tags instead of tojson_attr in x-data attributes
- Remove literal " from CSS selector in x-data (meta[name=csrf-token])
- Move Alpine.js defer script after extra_js block in base.html
- Add isinstance(resp, dict) guard before .get("error") in measure.py and maker.py
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -27,15 +27,15 @@ def recipe_list():
|
|||||||
|
|
||||||
resp = api_client.get("/api/recipes", params=params)
|
resp = api_client.get("/api/recipes", params=params)
|
||||||
|
|
||||||
if resp.get("error"):
|
if isinstance(resp, dict) and resp.get("error"):
|
||||||
flash(_("Errore nel caricamento delle ricette: %(error)s", error=resp.get("detail", "")), "error")
|
flash(_("Errore nel caricamento delle ricette: %(error)s", error=resp.get("detail", "")), "error")
|
||||||
recipes = []
|
recipes = []
|
||||||
total = 0
|
total = 0
|
||||||
pages = 0
|
pages = 0
|
||||||
else:
|
else:
|
||||||
recipes = resp.get("items", [])
|
recipes = resp.get("items", []) if isinstance(resp, dict) else resp
|
||||||
total = resp.get("total", 0)
|
total = resp.get("total", 0) if isinstance(resp, dict) else len(recipes)
|
||||||
pages = resp.get("pages", 1)
|
pages = resp.get("pages", 1) if isinstance(resp, dict) else 1
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"maker/recipe_list.html",
|
"maker/recipe_list.html",
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ def select_recipe():
|
|||||||
"""Recipe selection page with search and barcode support."""
|
"""Recipe selection page with search and barcode support."""
|
||||||
# Load recipes from API
|
# Load recipes from API
|
||||||
resp = api_client.get("/api/recipes", params={"per_page": 100})
|
resp = api_client.get("/api/recipes", params={"per_page": 100})
|
||||||
if resp.get("error"):
|
if isinstance(resp, dict) and resp.get("error"):
|
||||||
flash(
|
flash(
|
||||||
_("Errore nel caricamento delle ricette: %(detail)s",
|
_("Errore nel caricamento delle ricette: %(detail)s",
|
||||||
detail=resp.get("detail", "")),
|
detail=resp.get("detail", "")),
|
||||||
@@ -78,7 +78,7 @@ def task_list(recipe_id: int):
|
|||||||
|
|
||||||
# Load tasks for this recipe
|
# Load tasks for this recipe
|
||||||
tasks_resp = api_client.get(f"/api/recipes/{recipe_id}/tasks")
|
tasks_resp = api_client.get(f"/api/recipes/{recipe_id}/tasks")
|
||||||
if tasks_resp.get("error"):
|
if isinstance(tasks_resp, dict) and tasks_resp.get("error"):
|
||||||
flash(
|
flash(
|
||||||
_("Errore nel caricamento dei task: %(detail)s",
|
_("Errore nel caricamento dei task: %(detail)s",
|
||||||
detail=tasks_resp.get("detail", "")),
|
detail=tasks_resp.get("detail", "")),
|
||||||
@@ -154,7 +154,7 @@ def task_complete(recipe_id: int):
|
|||||||
"/api/measurements",
|
"/api/measurements",
|
||||||
params={"version_id": version_id},
|
params={"version_id": version_id},
|
||||||
)
|
)
|
||||||
if not meas_resp.get("error"):
|
if not (isinstance(meas_resp, dict) and meas_resp.get("error")):
|
||||||
measurements = (
|
measurements = (
|
||||||
meas_resp if isinstance(meas_resp, list)
|
meas_resp if isinstance(meas_resp, list)
|
||||||
else meas_resp.get("items", [])
|
else meas_resp.get("items", [])
|
||||||
|
|||||||
@@ -90,7 +90,7 @@
|
|||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
<div class="fixed top-16 right-4 z-50 flex flex-col gap-2 max-w-md w-full"
|
<div class="fixed top-16 right-4 z-50 flex flex-col gap-2 max-w-md w-full"
|
||||||
x-data="{ messages: {{ messages|tojson_attr }} }"
|
x-data
|
||||||
x-init="setTimeout(() => { $el.remove() }, 8000)">
|
x-init="setTimeout(() => { $el.remove() }, 8000)">
|
||||||
{% for category, message in messages %}
|
{% for category, message in messages %}
|
||||||
<div x-data="{ show: true }"
|
<div x-data="{ show: true }"
|
||||||
@@ -159,9 +159,9 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Alpine.js CDN (defer) -->
|
|
||||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
|
||||||
|
|
||||||
{% block extra_js %}{% endblock %}
|
{% block extra_js %}{% endblock %}
|
||||||
|
|
||||||
|
<!-- Alpine.js CDN (defer) - must load AFTER extra_js so component functions are defined -->
|
||||||
|
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
{% block title %}{{ _('Gestione Ricette') }} — TieMeasureFlow{% endblock %}
|
{% block title %}{{ _('Gestione Ricette') }} — TieMeasureFlow{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<script>window.__recipeListData = {{ recipes|tojson }};</script>
|
||||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-6xl"
|
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-6xl"
|
||||||
x-data="{
|
x-data="{
|
||||||
recipes: {{ recipes|tojson_attr }},
|
recipes: window.__recipeListData,
|
||||||
search: '{{ search or '' }}',
|
search: '{{ search or '' }}',
|
||||||
filter: 'all',
|
filter: 'all',
|
||||||
deleteModal: false,
|
deleteModal: false,
|
||||||
@@ -34,7 +35,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
async deleteRecipe(id) {
|
async deleteRecipe(id) {
|
||||||
const csrfToken = document.querySelector('meta[name=\"csrf-token\"]')?.content;
|
const csrfToken = document.querySelector('meta[name=csrf-token]')?.content;
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`/maker/api/recipes/${id}`, {
|
const resp = await fetch(`/maker/api/recipes/${id}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
{% block title %}{{ _('Seleziona Ricetta') }} — TieMeasureFlow{% endblock %}
|
{% block title %}{{ _('Seleziona Ricetta') }} — TieMeasureFlow{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<script>window.__selectRecipeData = {{ recipes|tojson }};</script>
|
||||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-7xl"
|
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-7xl"
|
||||||
x-data="{
|
x-data="{
|
||||||
recipes: {{ recipes|tojson_attr }},
|
recipes: window.__selectRecipeData,
|
||||||
search: '{{ auto_recipe_code }}',
|
search: '{{ auto_recipe_code }}',
|
||||||
lot_number: '{{ auto_lot }}',
|
lot_number: '{{ auto_lot }}',
|
||||||
serial_number: '{{ auto_serial }}',
|
serial_number: '{{ auto_serial }}',
|
||||||
|
|||||||
Reference in New Issue
Block a user