- Bottone "Avvio Produzione" appare solo dopo il primo ciclo confermato
- Conferma visiva "Produzione avviata" dopo il click
- Placeholder per futura integrazione GAIA (TODO nel codice)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Blueprint: passa measurement_interval_minutes dalla ricetta al template task_execute
- Dopo "Fine ciclo misura": countdown MM:SS con banner amber animato sopra footer
- Allo scadere: 3 beep sonori (Web Audio API 880Hz) + reset misurazioni per nuovo ciclo
- Contatore cicli visibile nel banner timer
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Recipe: nuovo campo measurement_interval_minutes (migration 003) con UI nel recipe editor
- Auto-logout: impostazione di sistema configurabile da admin/settings, timer inattività JS
con warning modale 60s prima del logout, tracking eventi utente throttled
- Navbar: aggiunto link "Impostazioni" per admin (desktop + mobile)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- setup: corregge path templates (sibling di api/, non figlio) e firma TemplateResponse
- setup: invalida sessione client dopo operazioni che ruotano l'API key (init DB, creazione admin, cambio password, attivazione utente)
- api_client: include il nome del campo nei messaggi di errore 422 (FastAPI validation)
- ui: nuovo componente _app_logo.html (SVG inline theme-aware) usato su login e navbar
- /measure/select: aggiunta miniatura 64×64 accanto al nome ricetta
(object-contain per mantenere l'immagine intera, non croppata).
Lo schema backend RecipeSummary esponeva solo id/code/name/active:
aggiunti image_path e description così la card può rendere thumb
e sottotitolo.
- /maker/recipes: la miniatura passa da object-cover (crop) a
object-contain (fit), per visualizzare l'intera immagine della
ricetta senza tagli.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Le liste "Ricette disponibili" e "Assegnate alla stazione" passano da
una lista con separatori interni (divide-y) a card individuali con
bordo arrotondato e spaziatura: migliora la leggibilità e la
percezione di trascinabilità dei singoli elementi.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pagina /measure/execute/<task_id>:
- Il pannello destro (info subtask + tolerance + numpad) e la sidebar
marker spariscono quando il task non ha subtask: l'utente vede solo
intestazione + disegno a tutta larghezza, senza colonne vuote né
tastierina numerica fuori contesto.
- Risolta la scrollbar verticale: la pagina ora occupa esattamente
l'altezza del viewport. Il layout sfrutta nuovi block override in
base.html (body_class, wrapper_class, main_class, footer) per
disattivare scroll e footer solo su questa pagina, lasciando intatte
le altre.
Cataloghi i18n: aggiunte traduzioni EN/IT mancanti e rimossi i flag
"fuzzy" auto-generati da pybabel update (Flask-Babel ignora i fuzzy,
mostrando il msgid italiano anche in EN).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
STATO_PROGETTO.md
- Bumped snapshot date to 2026-04-27.
- Added "Hardening post-restructure (smoke test 2026-04-26)" section
recording the four runtime regressions surfaced by the local smoke
test (env_file path, missing Station import, UPLOAD_DIR default,
apostrophe-in-translation in Alpine), plus the recipe-assignment
modal UX rework and the new node-based JS syntax test guard.
- Added "Smoke test status" section listing the verified end-to-end
flow (MySQL up, alembic, seed, login, admin pages, MeasurementTec
workflow, hot reload).
- Bumped frontend test count 44 → 46 to reflect
test_template_js_syntax.py.
ROADMAP.md
- Added a tech-debt entry for the user-reported task_complete
riepilogo rendering anomaly (still under investigation: the curl
fetch returns the table populated, but the user reports an empty
body in the browser).
- Added a tech-debt entry for the still-pending Docker container
smoke test of the new uv-based Dockerfiles.
src/frontend/flask_app/templates/measure/task_complete.html
- Replaced sort(attribute='task_info.order_index,subtask.marker_number')
with two chained stable sorts. Jinja's sort filter does not accept a
comma-separated multi-attribute string; the previous form sorted on
a non-existent attribute and only worked by accident because the
API already returned rows in the desired order.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
UX rework — recipe assignment modal on /admin/stations
- Replace the single "select recipe + Assegna" dropdown with a two-
column layout: Ricette disponibili (left) and Assegnate alla
stazione (right), each row with an inline action button. Top search
filter narrows both columns at once. Empty states explain *why* the
list is empty (no recipes in system, all already assigned, no match
for the filter).
- Rationale: the old dropdown silently hid every option once a recipe
was assigned, leaving the user unable to tell whether the system
was broken or simply out of unassigned recipes.
Apostrophe regressions
- /admin/stations alert/errorMsg literals reworded with double-quoted
outer JS strings ("Errore nella eliminazione" / "...assegnazione").
- /admin/users toggle confirm modal: x-text expression contained
'{{ _('… l\'utente') }}'. Inside a Jinja-rendered HTML attribute,
the apostrophe in "l'utente" closed the JS literal early, killing
the binding. Fixed by using " as the JS string delimiter so
the inner apostrophe is harmless.
Alpine x-if templates can't host nested templates
- Replaced two nested-template empty-state blocks with x-text bound
to computed getters (unassignedEmptyMessage,
assignedEmptyMessage). Alpine errored with
"Cannot set properties of null (setting '_x_dataStack')" when the
outer template's child wasn't a single root element.
Test guard widened
- src/frontend/flask_app/tests/test_template_js_syntax.py now also
parses every Alpine attribute (x-*, @*, :*) on the rendered HTML
and runs `node --check` on each expression wrapped in `void (…)`.
Previously it only inspected inline <script> bodies, which is why
the x-text bug on /admin/users slipped through. Verified the
extended test catches the original l'utente regression by reverting
+ running + restoring.
Layout regression — UPLOAD_DIR defaulted to server/uploads
- The previous .env.example shipped UPLOAD_DIR=server/uploads, which
matched the V1.x layout but pointed outside the new project tree.
Updated to UPLOAD_DIR=uploads so files land in the project-root
uploads/ volume that src/backend/config.py.upload_path resolves.
- Added uploads/general/ to .gitignore (per-user uploads, not source).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two error messages on /admin/stations rendered Italian translations
that contain an apostrophe inside a single-quoted JS literal:
alert(result.detail || '{{ _("Errore nell\'eliminazione") }}');
Jinja outputs the apostrophe verbatim, so the JS string closed
prematurely:
alert(result.detail || 'Errore nell'eliminazione');
That syntax error blew up the entire <script> block, which means
stationManagement() was never defined and EVERY Alpine binding on the
page failed silently. Symptom: clicking "Nuova Stazione" did nothing,
DevTools showed "Alpine Expression Error: openCreateModal is not
defined".
Fix: swap outer JS quotes to double quotes and reword the IT string so
the apostrophe disappears anyway ("Errore nella eliminazione",
"Errore nella assegnazione"). Same for the assignment-error path.
Regression guard: src/frontend/flask_app/tests/test_template_js_syntax.py
renders /admin/stations and /admin/users with the IT locale forced,
extracts every inline <script>, and runs `node --check` on each. The
test is skipped if `node` is not on PATH so CI without Node still
passes. Verified the test catches the original bug (revert + run +
fail) before re-applying the fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aligns the repo with the python-project-spec-design.md template chosen
for V2.0.0. Big move, no logic changes. The 3 pre-existing test
failures (test_recipes::test_update_recipe, test_recipes::
test_recipe_versioning, test_tasks::test_reorder_tasks, plus the
client test_save_measurement_proxy) survive unchanged.
Layout changes
- server/ -> src/backend/
- server/middleware/ -> src/backend/api/middleware/
- server/routers/ -> src/backend/api/routers/
- server/models/ -> src/backend/models/orm/
- server/schemas/ -> src/backend/models/api/
- server/uploads/ -> uploads/ (project root, mounted volume)
- server/tests/ -> src/backend/tests/
- client/ -> src/frontend/flask_app/ (Flask kept; React
deroga is documented in CLAUDE.md, justified by tablet UX, USB
caliper/barcode workflow and Fabric.js integration)
Tooling
- pyproject.toml: monorepo with [project] core deps and
optional-dependencies server / client / dev. Replaces both
server/requirements.txt and client/requirements.txt.
- uv.lock + .python-version (3.11) committed for reproducible builds.
- Dockerfile (root, backend) and Dockerfile.frontend rewritten to use
uv sync --frozen --no-dev --extra server|client; legacy Dockerfiles
preserved as Dockerfile.legacy for reference but excluded from build
context via .dockerignore.
- docker-compose.dev.yml + docker-compose.yml: build context now ".",
dockerfile pointing to the root files.
Code adjustments forced by the move
- Every "from config|database|models|schemas|services|routers|middleware
import ..." rewritten to its src.backend.* equivalent (50+ files
including indented inline imports inside test bodies).
- src/backend/migrations/env.py: insert project root into sys.path so
alembic can resolve src.backend.* imports regardless of cwd.
- src/backend/config.py: env_file ../../.env (was ../.env), upload_path
resolves project root via parents[2].
- src/backend/tests/conftest.py + tests: import ... from src.backend.*
instead of bare names; old per-directory pytest.ini files removed in
favor of root pyproject.toml [tool.pytest.ini_options].
- .gitignore: uploads/ at root, src/frontend/flask_app/static/css/
tailwind.css path; .dockerignore tightened.
- CLAUDE.md: rewrote sections "Layout del repository", "Comandi di
Sviluppo", "Database & Migrations", "Test", "i18n", and all path
references throughout the architecture sections.
Verified
- uv lock resolves 77 packages; uv sync --extra server --extra client
--extra dev installs cleanly.
- uv run pytest: 171 passed, 4 pre-existing failures.
- uv run alembic -c src/backend/migrations/alembic.ini check loads
config and metadata (errors only on the absent local MySQL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>