Commit Graph

66 Commits

Author SHA1 Message Date
Adriano 2a2d40bec9 fix(admin): two more apostrophe-in-translation regressions + UX rework
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>
2026-04-26 14:49:23 +02:00
Adriano 6e284b0c0c fix(admin): apostrophe-in-translation broke /admin/stations Alpine bindings
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>
2026-04-26 12:32:32 +02:00
Adriano 742cc1fb58 fix(v2): two regressions surfaced by the local smoke test
Both came from the src/ restructure and only show up at runtime, so
the test suite had not caught them.

- src/backend/config.py: env_file was "../../.env", which pydantic-
  settings resolves against the *cwd*, not the file. Running uvicorn
  or alembic from the project root therefore looked for
  ../../.env one level above the repo and silently fell back to the
  default DB_PASSWORD ("change_me_in_production"), hiding the real
  password. Now resolved as Path(__file__).resolve().parents[2] /
  ".env" so the lookup is always against the project root regardless
  of cwd.

- src/backend/models/orm/__init__.py: Station and
  StationRecipeAssignment were never imported here, so anything that
  triggers Base.metadata.create_all without first importing the
  setup router (which has its own Station import) ended up with no
  stations / station_recipe_assignments tables. Verified locally:
  /api/setup/seed used to fail with "Table tiemeasureflow.stations
  doesn't exist" before this fix.

- .gitignore: ignore src/frontend/flask_app/package.json and
  package-lock.json (local npm-install artifacts; the Dockerfile
  installs tailwindcss directly).

Smoke verified end-to-end: uvicorn + gunicorn + MySQL, login + admin
stations + select_recipe + admin users all 200 OK.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 12:19:36 +02:00
Adriano 563b7789f4 docs(readme): rewrite for V2.0.0 — uv, src/ layout, stations, docs index
Brings the entry-point README in line with the V2.0.0 restructure:

- Replace all server/ + client/ paths with src/backend/ +
  src/frontend/flask_app/.
- Replace pip install -r requirements.txt with the uv workflow
  (uv sync --extra server --extra client --extra dev).
- Manual setup section uses uv run uvicorn / uv run flask /
  uv run alembic / uv run pybabel, all driven from the project root.
- Document the V2.0.0 additions: STATION_CODE per-tablet, /admin/
  stations GUI, gunicorn 5x4 + uvicorn 4 worker scaling, X-Forwarded
  -For-aware rate limiting (RATE_LIMIT_GENERAL default 300).
- Add tooling section (uv, pyproject.toml, uv.lock, .python-version,
  pytest stack).
- Documentation section now points at the new docs/ index plus the
  STATO_PROGETTO + ROADMAP architecture pair as the canonical "what
  is done / what is next" references.
- Variabili d'Ambiente: add STATION_CODE, RATE_LIMIT_LOGIN,
  RATE_LIMIT_GENERAL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:48:51 +02:00
Adriano e4b29c0b2d chore(docs): consolidate documentation, drop dead files
Cleanup
- Remove src/backend/Dockerfile.legacy and
  src/frontend/flask_app/Dockerfile.legacy (history is in git, build
  uses the new uv-based root Dockerfile / Dockerfile.frontend).
- Remove src/frontend/flask_app/verify_i18n.py (had hardcoded paths
  pointing at the old client/ tree).

Group docs/
- New docs/README.md indexes everything in one place.
- New docs/architecture/STATO_PROGETTO.md: snapshot of what works in
  V2.0.0 (inherited V1.0.7 features, rev04 Phase 1 stations,
  worker scaling, src/ restructure, test status, stack, decisions).
- New docs/architecture/ROADMAP.md: what's next — Phases 2-7 of the
  rev04 migration with status, open client decisions (D-0.1 through
  D-0.10), tech debt and time estimates for M1 / M2.
- Move PIANO_IMPLEMENTAZIONE.md (90KB V1.0.0 plan) to
  docs/archive/2026-02-06-piano-implementazione-v1.md (historical).
- Move Schema sviluppo SW TieFlow_rev04-2026.docx to docs/specs/
  with ISO date filename so the customer spec is now tracked.
- Move src/frontend/flask_app/I18N_SETUP.md to docs/I18N_SETUP.md
  and rewrite paths to the new src/frontend/flask_app/ tree.

.dockerignore: simplified now that legacy Dockerfiles are gone;
docs/ stays excluded from the build context.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:43:57 +02:00
Adriano 1a0431366f chore(v2): restructure monorepo to src/ layout with uv
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>
2026-04-25 12:26:47 +02:00
Adriano 86df67f2e5 perf: scale workers + per-tablet rate limiting for 20 concurrent users
The default 2-worker gunicorn could only serve 2 concurrent tablet requests,
queueing the rest, and the rate limiter saw every tablet as the same Nginx
container IP, so 20 users would have collectively burned through the
100 req/min general bucket.

- gunicorn: 5 workers x 4 gthread, --forwarded-allow-ips=*, access log
- uvicorn: 4 workers, --proxy-headers, --forwarded-allow-ips=*
- RateLimitMiddleware: resolve real client IP from
  X-Forwarded-For -> X-Real-IP -> request.client.host
- Bump rate_limit_general 100 -> 300 req/min/IP (per tablet now)
- Flask: ProxyFix(x_for=1, x_proto=1, x_host=1) so request.remote_addr
  is the tablet IP, not the Nginx IP
- APIClient: forward X-Forwarded-For + X-Real-IP to FastAPI for both
  JSON and multipart/files calls; safe no-op outside request context
- 12 new tests (7 server + 5 client) covering header precedence,
  forwarding behavior and ProxyFix install

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:07:43 +02:00
Adriano ea8e4687b5 merge: rev04 Phase 1 — stations and per-tablet identity (feature/rev04-phase1-stations into V2.0.0) 2026-04-25 11:54:51 +02:00
Adriano a6c335ca8b feat(client): add admin GUI for stations CRUD and recipe assignments
Adds a complete browser-based interface for managing stations,
closing the last deliverable of rev04 Phase 1.

- New /admin/stations page with stations table, create/edit modal,
  delete confirmation and dedicated recipe-assignment modal
- Proxy endpoints under /admin/api/stations/* covering CRUD and
  recipe assign/unassign so all admin operations stay behind the
  Flask CSRF + admin_required guard
- Navbar entry "Stazioni" (desktop + mobile), visible to admins only
- 10 new tests covering page render, every proxy and the non-admin
  redirect

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:50:33 +02:00
Adriano 946264637b feat(client): filter select_recipe by STATION_CODE with error fallback
Replace generic /api/recipes call with api_client.get_station_recipes(STATION_CODE).
Return 503 station_not_configured.html when STATION_CODE env var is unset.
Add station indicator to recipe selection page header.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 08:28:46 +02:00
Adriano a4a849920f feat(client): add get_station_recipes helper on APIClient
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 23:24:19 +02:00
Adriano 958f6ac0b0 feat(client): add STATION_CODE env var and config attribute
Reads STATION_CODE from the environment and exposes it as Config.STATION_CODE
(None when unset or empty). Adds the variable to .env.example with a
per-station deployment note, and covers both read and missing-key paths with
new pytest tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 23:18:24 +02:00
Adriano 2e4db53f6a feat(setup): seed ST-DEFAULT station and assign existing recipes
Add _seed_default_station() helper to /api/setup/seed endpoint that
creates the ST-DEFAULT station and assigns all active recipes to it,
preserving existing behaviour after migration 002 runs on live DBs.
Helper is idempotent and is called on both the normal seed path and
the early-return path (when demo data already exists).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 23:04:41 +02:00
Adriano a79ab37add fix(api): improve Station router - review feedback
- Rinomina _RecipeSummary -> RecipeSummary: il leading underscore
  segnalava "privato" ma la classe e usata come response_model pubblico
  ed esposta nell'OpenAPI schema.
- Aggiunge commento esplicativo sopra /by-code/{code}/recipes sul perche
  l'ordine di dichiarazione conta (protezione gia data dal tipo int di
  station_id, ma esplicito per prevenire regressioni durante refactor).
- Detail message del 404 by-code uniformato a "Station not found"
  (senza distinguere not-found vs inactive, evita leak di esistenza).
- Aggiunge 3 test mancanti sul router:
  * test_admin_can_list_stations (copertura happy path + active_only)
  * test_assign_recipe_not_found_returns_404
  * test_duplicate_assignment_returns_409

Feedback da code-reviewer su Task 5. Full suite: 11/11 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 22:46:54 +02:00
Adriano 338f21fba0 feat(api): add /api/stations router with CRUD and assignments
Implements the /api/stations FastAPI router (admin-only CRUD, recipe
assignment endpoints) and the public /by-code/{code}/recipes operator
endpoint. Registers the router in main.py and adds 8 integration tests.
2026-04-17 22:37:16 +02:00
Adriano e8cd1f05aa feat(services): add station_service with CRUD and assignment logic
Implements create/update/delete station, assign/unassign recipe,
list_station_recipes (active only, ordered by code), and get_station_by_code.
Explicit ORM-level assignment deletion in delete_station ensures cascade
works engine-agnostically (SQLite tests + MySQL production). 10 TDD tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 22:31:16 +02:00
Adriano 559e740d64 feat(schemas): add Station and assignment Pydantic schemas 2026-04-17 22:25:42 +02:00
Adriano e36bbbb7d7 fix(models): align Station unique constraint + extend tests
- Station.code: usa UniqueConstraint("code", name="uq_stations_code")
  esplicito in __table_args__ invece di unique=True sulla colonna,
  per allineamento con la migration 002 ed evitare drift Alembic.
- Aggiunge test test_duplicate_assignment_is_rejected per coprire
  il vincolo uq_station_recipe (regola business centrale del modello).
- Sposta import IntegrityError a module-level per consistenza.

Feedback da code-reviewer su Task 2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 22:22:14 +02:00
Adriano 5959c9c92a feat(models): add Station and StationRecipeAssignment models
TDD: test written first, confirmed failing with ModuleNotFoundError,
then model implemented; all 3 new tests pass. conftest updated to
import new models so Base.metadata.create_all picks up the tables.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 21:56:22 +02:00
Adriano 2963d3d647 fix(db): portability and dedup in migration 002
- server_default='1' anziche sa.true() per compatibilita con SQLite
  (usato come DB in-memory nei test)
- Rimuove Index ix_stations_code ridondante con UniqueConstraint
  uq_stations_code (InnoDB crea gia un indice per i vincoli UNIQUE)

Feedback da code-reviewer su Task 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 21:40:15 +02:00
Adriano fa5d641238 chore: track alembic migrations in git
Le migrations Alembic sono essenziali per il deploy riproducibile:
rimuove la regola in .gitignore che le escludeva e aggiunge al
tracking la migration 001 (image_path) gia esistente ma mai committata.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 21:30:27 +02:00
Adriano abd04d633c feat(db): add migration 002 for stations and assignments 2026-04-17 21:17:14 +02:00
Adriano f71bbf398e docs: add rev04 migration master roadmap and phase1 plan
Aggiunge il master plan di migrazione V1.0.7 -> V1.1.0 (rev04-2026)
con le sette fasi organizzate in milestone M1 (demo cliente) e M2
(produzione), piu il piano TDD dettagliato della Fase 1 (Stazioni e
identita per-tablet) con 17 task eseguibili.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 21:13:01 +02:00
Adriano 767bd7f947 docs: fix middleware order and add missing details to CLAUDE.md
Correct the middleware stack order (was documented backwards due to
Starlette's add_middleware wrapping behavior), document fabric-debug.js
local copy, and note full 50-900 shade palettes for Tailwind colors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 14:24:02 +01:00
Adriano 1578398f0f docs: rewrite README with cleaner structure and less duplication
Restructure README to be more concise: add feature overview section,
architecture diagram with security note, tabular stack listing,
streamlined quick start, and env vars reference table. Move VPS
deployment details to docs/DEPLOYMENT.md to avoid duplication.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 12:00:47 +01:00
Adriano 429c94da94 fix: improve SPC dashboard UX — hover toolbar, visible report errors
- Change Plotly modebar to hover-only mode to avoid overlapping chart content
- Remove redundant Plotly titles (container headers already provide them)
- Add Content-Type check and inline error display for report downloads
- Fix setup login validation and seed idempotency for existing data

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 16:42:47 +01:00
Adriano e07a4e4f89 feat: add demo measurements to seed and user management to setup page
Seed now generates 180 realistic measurements (30 per subtask) with
Gaussian distribution for SPC testing. Setup page gains full user CRUD
with list, create/edit, password change, and activate/deactivate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 14:49:58 +01:00
Adriano 272685e554 docs: fix inaccuracies and add missing details to CLAUDE.md
Correct client conftest patching (4 blueprints, not 5 - admin excluded),
add middleware stack order with CORSMiddleware, fix RecipeVersionAudit
enum (add ACTIVATE), clarify server/client Docker workers (uvicorn vs
gunicorn), document tojson_attr filter, measure AJAX routes, Tailwind
custom colors, and dark mode config.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 14:03:57 +01:00
Adriano e9a3e8e42b fix: rewrite subtask editing to reuse add form instead of inline table row
Edit button now opens the same "Nuova Misurazione" form pre-filled with
existing data, showing "Modifica Misurazione" title and "Aggiorna Misurazione"
button. Also adds marker_number to SubtaskUpdate schema so edits persist.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 19:07:55 +01:00
Adriano ecf700eadf feat: improve recipe preview image UI with replace/remove buttons
Replace drop-zone with action buttons (Sostituisci/Rimuovi) when image
exists, matching the task editor pattern. Add upload overlay with
spinner on the image during file upload.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 19:10:59 +01:00
Adriano dbfb5591c5 fix: separate recipe preview image from task images
Recipe image_path is now used as preview thumbnail only. Removed
auto-creation of "Technical Drawing" task from recipe upload, and
removed recipe image strip from task_execute view. Each task displays
its own file_path independently.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 18:29:27 +01:00
Adriano 22804a609d feat: add production docker-compose with Traefik reverse proxy
Production compose with Traefik labels for auto-SSL via Let's Encrypt.
Requires external traefik-net (root_default) network.
Dev compose (docker-compose.dev.yml) remains unchanged with Nginx.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 11:59:01 +01:00
Adriano 6bd6e229e5 feat: redesign task_execute with 3-column layout and subtask images
Refactor measurement execution page from 2-column to 3-column layout:
- Left: vertical marker sidebar with status indicators
- Center: image area with subtask image switching (per-subtask detail
  images override task annotation viewer) + optional recipe image strip
- Right: compact info panel with tolerances, feedback, and numpad

Also: compact header bar, use window.__data pattern for tojson escaping,
fetch recipe image_path in measure blueprint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 11:58:55 +01:00
Adriano 13986f05d7 feat: add admin user management page with CRUD and i18n
- New admin blueprint with CRUD proxy endpoints for users
- Admin user management template with search, create, edit, toggle active
- Navbar: add admin link for is_admin users (desktop + mobile)
- Register admin blueprint in app factory
- Add IT/EN translations for all admin UI strings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 11:58:47 +01:00
Adriano 5cc576cec9 feat: add image_path to recipe/subtask and user password change API
- Recipe model: add image_path field for recipe-level image
- RecipeSubtask model: add image_path for per-subtask detail images
- Schemas: add image_path to create/update/response for recipe and subtask
- Task router: pass image_path when creating tasks and subtasks
- Recipe service: copy image_path in versioning and update-in-place
- Users router: add PUT /{user_id}/password endpoint (admin only)
- User schema: add UserPasswordChange model

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 11:58:41 +01:00
Adriano 08bcb87d6b docs: update README with Traefik deployment, correct blueprints, fix commands
- Architecture: document dual compose (dev Nginx / prod Traefik)
- Stack: remove htmx (unused), pin Fabric.js to 5.3.1
- Fix blueprint names (measure, statistics, admin instead of measurement, metrologist)
- Fix alembic commands with -c migrations/alembic.ini flag
- Fix .env variable names to match actual config
- Update testing section with real examples (not "da implementare")
- Replace Certbot/Nginx SSL section with Traefik auto-SSL
- Document is_admin capabilities (user CRUD)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 11:57:04 +01:00
Adriano 6c8c60900b docs: update CLAUDE.md with admin blueprint, dual docker-compose setup
Align documentation with current codebase state: add admin blueprint to
client architecture, document dev (Nginx) vs prod (Traefik) compose files,
fix blueprint count in test section.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 11:53:25 +01:00
Adriano 945c873d38 docs: update CLAUDE.md with conditional versioning, measurement workflow, and JS components
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 13:48:04 +01:00
Adriano 9502388064 docs: improve CLAUDE.md with architecture details, patterns, and fixes
Fix Alembic commands (add -c migrations/alembic.ini flag), add Fabric.js
annotation editor patterns, Alpine.js/Jinja2 escaping rules, template
structure, test infrastructure details, and Docker configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 21:25:16 +01:00
Adriano e4c2cc3ed4 chore: exclude .omc/ from git and fix Fabric.js version in README
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 19:39:14 +01:00
Adriano e8b88d48c1 feat: measurement workflow improvements and recipe update-in-place
- Auto-advance to next task after completing all subtask measurements
- 1s pause between measurements to show pass/fail/warning result
- Colored marker strip (green/red/amber) based on measurement status
- Replace duplicate measurements instead of appending (fixes progress bar)
- Add Task column and Date/Time column to measurement summary table
- Enrich summary with task_info for each measurement
- Update-in-place for recipe versions without measurements (no copy-on-write)
- Dark theme improvements and navbar cleanup
- Server config: ignore extra env vars

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 19:36:42 +01:00
Adriano 4854966bf7 chore: add fabric-debug.js and nul to .gitignore
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:15:47 +01:00
Adriano 9e1bb20b36 fix: canvas fits image exactly, chained annotation loading, arrow move tracking
- Canvas now sizes to match the scaled image dimensions (zero empty space)
  instead of using a fixed aspect ratio, fixing coordinate mismatch between
  editor and viewer
- Annotations load only after background image is ready via _pendingAnnotations
  pattern, preventing placement at wrong coordinates
- Arrow endpoints (arrowX1/Y1/X2/Y2) update on object:modified using
  transform delta, so moved arrows serialize at correct position
- Coordinate scaling on load: coordScale = currentImageScale / savedImageScale
  handles annotations saved at different screen widths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:10:21 +01:00
Adriano f2df6be060 fix: enable resize/rotate controls on single-click object selection
In setMode('select'), explicitly set hasControls, hasBorders, and evented
on all objects. In drawing modes, disable selectable/evented to prevent
accidental interaction. Fixes controls only appearing on multi-select drag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 10:45:56 +01:00
Adriano f665bffb7a fix: Task back button navigates to correct recipe (not version_id)
- Add recipe_id property to RecipeTask model (via version relationship)
- Add recipe_id to TaskResponse schema
- Eager-load version in _get_task_or_404 query
- Use task.recipe_id instead of task.version_id in task_execute template URLs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 10:34:23 +01:00
Adriano 00d6c68b2f fix: enable scale/rotation on annotation objects and restore transforms on load
- Remove hasControls:false and lockScaling from markers so resize/rotate handles appear
- Add setActiveObject() after creating arrows and rectangles for immediate control visibility
- Restore angle, scaleX, scaleY when loading saved annotations (all object types)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 10:29:30 +01:00
Adriano dfc1c3ed95 fix: i18n translations and annotation property changes on selected objects
- Complete English translation catalog (44 entries fixed/added)
- Fix Fabric.js Group cache invalidation (active.dirty = true) so color,
  thickness and line-dash changes are visible on selected markers/arrows
- Fix Italian .po placeholder mismatch (%(detail)s -> %(error)s)
- Bump annotation-editor.js cache buster to v8

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 01:44:46 +01:00
Adriano 582543e821 fix: bump annotation-viewer.js cache buster to v4
Browser was serving stale cached version without editor format
support (objects array). Bumped ?v=4 on all three templates:
recipe_preview, task_execute, task_editor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 01:26:20 +01:00
Adriano ffec59a105 fix: annotation overlay in recipe preview and measure view
annotationViewer.drawAnnotations() now handles the editor format
(objects array with marker/arrow/area) in addition to the legacy
format (markers array). This fixes missing overlay in recipe preview
and task execution views.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 01:22:30 +01:00
Adriano 6ea94cca47 feat: per-task image/annotations, annotation editor toolbar, Tailwind compiled
- Add per-task file upload with image preview in task editor
- Add dedicated annotation editor page (task_drawing.html) with Fabric.js
- Add color picker, stroke width, and line dash controls to annotation toolbar
- Apply property changes to selected objects in real-time
- Disable style controls until a drawing tool or object is selected
- Remove zoom/pan from annotation toolbar (simplified UX)
- Auto-switch to select mode after placing annotation elements
- Show annotation overlay on task image previews (read-only canvas)
- Add file proxy route in measure blueprint for task file access
- Add file_path/file_type fields to TaskCreate/TaskUpdate Pydantic schemas
- Replace Tailwind CDN with compiled CSS (tailwind.config.js with full shades)
- Fix Alpine.js x-init crash: extract annotations JSON to <script> tags
  (recipe_preview.html, task_execute.html) to avoid HTML attribute breakage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 01:20:34 +01:00