Files
kreios/CLAUDE.md
T
carmine.savino 0dfda3a338 Refactor PVC models and update tests
- Updated the PVC models in the database and adjusted the tests accordingly to reflect the new model counts and codes.
- Removed deprecated models from the JSON data for PVC systems.
- Added new GLB models for "scorrevole_sf_3_ante" and "scorrevole_sf_4_ante".
- Enhanced the development scripts to streamline the build process for both frontends.
- Updated documentation to reflect changes in the development environment and deployment process.
- Improved the styling of texture radio options for better visibility.
- Fixed URL handling in the configurator to ensure proper routing and API calls.
2026-10-20 18:40:59 +02:00

102 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project overview
Web-based 3D configurator for infissi (windows/doors) — "kreiosinfissi-vite". It renders a Three.js scene
(GLB models) driven by JSON product data and a jQuery-based menu UI, and is meant to run **inside an
`<iframe>`** embedded on `https://www.kreiosinfissi.it` (or `https://kreiosinfissi.test` in dev), with the
final "add to cart" action posted back to the parent window via `postMessage`.
## Commands
Package manager is **pnpm** (`pnpm-lock.yaml` is the lockfile of record even though `package-lock.json` is
also present).
```bash
pnpm install # install deps
./dev.sh # single server: Laravel on :8000 + both frontends in `vite build --watch`
./build.sh # build configuratore + app cliente into backoffice/public
pnpm build # configuratore only -> backoffice/public/configuratore (root is src/)
```
Everything is served by Laravel on one origin: configuratore at `/configuratore/?sistema=…&type=…`, quote
app (`backoffice/cliente`, Vue) at `/preventivo/`, API at `/api`, admin at `/admin`. The `index.html` of each
frontend is served by routes in `backoffice/routes/web.php`; there is no HMR, reload after the rebuild.
There is no test suite (`pnpm test` is a stub that exits with an error) and no linter configured.
### Regenerating PVC price lists
`scripts/extract_listini_pvc.py` parses `PVC_LISTINO.xls` and regenerates
`src/assets/data/modelli/pvc/<modello>/listino.json` files. Requires a Python venv with `xlrd`
(`pip install -r scripts/requirements.txt`). Run with `python3 scripts/extract_listini_pvc.py` from repo root.
Any new title in the spreadsheet needs an entry in that script's `KNOWN_FOLDERS` map or it won't be matched
to an existing model folder.
## Architecture
Entry point is `src/js/starter.js`, loaded from `src/index.html`. Flow on load (`start()`):
1. **`ParamsParser.getQueryParams()`** (`src/js/params_parser.js`) reads the URL query string. Two params
drive everything: `sistema` (`pvc` | `alluminio` | `legno` | `acciaio`) and `type` (model id). See
`docs/URL_PARAMS.md` for the full param contract and the list of valid `type` values per `sistema`.
*Note: that doc describes a more modular refactor (`Application.js`, `communication/UrlParamsParser.js`,
`communication/IframeBridge.js`) that does not exist yet in `src/js` — the current implementation is the
flatter `starter.js`/`params_parser.js` described here.*
2. Catalog data comes from the Laravel backoffice (`backoffice/`, API base in `src/js/api_base.js`):
`GET /api/sistemi` (systems + active models that have a price list; models without one are hidden and flagged in the admin list) and
`GET /api/sistemi/<sistema>/modelli/<modello>/configurazione`, which returns the menu wiring (`form`: radios,
selects, finish menus — the old `form.json` shape), finishes, handles, glass types and the price-grid sizes,
with image URLs served by `GET /api/media/...` (CORS-enabled, needed for WebGL textures). Only
`avvolgibile.json` and `sistemi/<sistema>/altezza_maniglia_<categoria>.json` are still read from `src/assets/data`.
3. **`MenuBuilder.build()`** (`src/js/menu_builder.js`) injects an HTML template (`assets/_menu.html`, or
`assets/_menu-cassonetto.html` when `type=cassonetto`) into `#left-container`, builds the model-picker
list, handles the mobile-portrait/landscape layout reflow, and initializes `PriceCalculator`. It returns
the price calculator instance used by the rest of the app.
4. **`Configurator.init()` / `.enable()`** (`src/js/configurator.js`) wires a single delegated `input` handler
on the size fields, selects and radios. On every change (debounced) it sends the selected codes to
`POST /api/sistemi/<sistema>/modelli/<modello>/prezzo`, stores the result in `window.configurazione` and
updates the DOM. "Add to cart" sends the same codes (`scelte`): the backend recomputes the price and ignores
any price coming from the client.
5. **`APP.Player`** (`src/js/app.js`) wraps a Three.js renderer/scene/camera (scene graph loaded from
`src/assets/app.json`, a three.js-editor-style export) plus the actual product GLB
(`src/assets/models/<sistema>/<modello.modello>`). Two players are created: `player` (visible canvas) and
`playerOff` (offscreen, used only to render the PNG snapshot sent to the cart). Key methods: `swapModel`/
`hideModel` (toggle between pre-loaded variants in the same scene), `changeTexture`/`changeColor` (applied
per `userData.categoria` mesh tag — categories like `interno`, `esterno`, `maniglia`, `vetro`, `cerniere`),
`flipView` (180° GSAP-animated turn between front/back view), `addMarker` (in-scene hotspot with a video
popup).
6. **Pricing is server-side only**: `backoffice/app/Support/Configuratore/CalcolatorePrezzo.php`.
Computed in cascading phases, each phase's percentages applied to the previous phase's total (VAT
excluded): `serramento = base × (1 + finitura %)`, then profili extra on the serramento (measures +
finishes only), then the other options (telaio, apertura, accessori), then `+ variazione vetro`. Base is
the first grid size ≥ the requested one (mm). Supplements come from `opzioni` rows (finish type
bianco/uniforme/bicolore, hinges, profili extra, ...): percentage, fixed, or per linear metre
(ml from L/H multipliers + extra mm). Groups with `ui = multipla` (profili extra) accept several codes,
with same-`alternativa` options mutually exclusive. Sistema-level rows can be overridden per model by group.
Option groups themselves live in `gruppi_opzioni` (title, control radio/select/multipla, menu section, and
the model `moduli` key that enables them): a group without fixed markup in `_menu.html` is rendered by
`MenuBuilder` into `.opzioni-sistema` / `.opzioni-accessori`. Select options can call a `window[func]`
(e.g. `nascondiCategoria` with `categoria: maniglia` for the motor) via their `parametri`. The glass
variation is Σ max(pane area, minimo_mq) × maggiorazione_mq, with panes from the model's `regole_vetro`
(spec: "Specifica tecnica vetri"). All of it is edited in Filament under "Configuratore".
`src/js/price_calculator.js` only wraps the API call and the grid sizes.
### Data conventions
- Every 3D-visible part is tagged with `userData.categoria` in the GLB/scene so JS can show/hide/retexture it
without knowing mesh names (`interno`, `esterno`, `maniglia`, `cerniere`, `vetro`, ...).
- Which menu sections a model shows (and which items enter the price) is driven by its `moduli` in the
backoffice (`Modello::MODULI`, e.g. `finitura_esterna`, `apertura`, `telaio`, `vetro`). The per-model JSON
files under `src/assets/data/modelli/` are legacy and no longer read (except `avvolgibile.json`).
- Texture and icon images live in the backoffice (`storage/app/public/configuratore/...`, seeded once by
`ConfiguratoreSeeder` from `backoffice/database/seeders/data/configuratore/`) and are loaded lazily per
selection (`APP.Player.changeTexture`) from absolute URLs; a monotonically-checked `isStale()` guard discards
a texture load if the user changed the selection again before it finished.
- `window.*` globals (`window.configurazione`, `window.cambiaTexture`, `window.caricaModello`,
`window.rotate`, `window.sistema`, etc.) are the integration surface between the jQuery-driven menu markup
(inline `onclick`/`onchange` in the `_menu*.html` templates) and the JS modules — expect to grep for a
`window.<name> =` assignment when tracing menu behavior, not for exported module functions.