Init claude e impeccable
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"enabledPlugins": {
|
||||
"impeccable@impeccable": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"generatedAt": "2026-07-08T00:00:00.000Z",
|
||||
"title": "Design System: SteUP",
|
||||
"extensions": {
|
||||
"colorMeta": {
|
||||
"primary": { "role": "primary", "displayName": "Arancio-Rosso Allerta", "canonical": "#ec4c41", "tonalRamp": ["#3a0f0b", "#5e1712", "#8a2019", "#b62f24", "#ec4c41", "#f27a72", "#f7a9a3", "#fcdcd9"] },
|
||||
"secondary": { "role": "secondary", "displayName": "Blu Profondo", "canonical": "#002339", "tonalRamp": ["#000c14", "#001622", "#002339", "#003653", "#0a5480", "#3f7ea8", "#87b3d0", "#d5e6f1"] },
|
||||
"tertiary": { "role": "tertiary", "displayName": "Azzurro Tenue", "canonical": "#dff2ff", "tonalRamp": ["#12313f", "#1c5570", "#2b7ba3", "#4aa4d1", "#86c8e8", "#b3ddf3", "#dff2ff", "#f2faff"] },
|
||||
"ink": { "role": "neutral", "displayName": "Inchiostro", "canonical": "#000000", "tonalRamp": ["#000000", "#1a1a1a", "#333333", "#4d4d4d", "#737373", "#999999", "#cccccc", "#f5f5f5"] },
|
||||
"paper": { "role": "neutral", "displayName": "Carta", "canonical": "#ffffff", "tonalRamp": ["#c2c2c2", "#d1d1d1", "#e0e0e0", "#ededed", "#f5f5f5", "#fafafa", "#ffffff", "#ffffff"] },
|
||||
"success": { "role": "semantic", "displayName": "Verde OK", "canonical": "#26b050", "tonalRamp": ["#0a3318", "#125025", "#1a7d38", "#26b050", "#4fca72", "#84dc9d", "#b7ecc6", "#e6f9ec"] },
|
||||
"error": { "role": "semantic", "displayName": "Rosso Errore", "canonical": "#e50000", "tonalRamp": ["#3a0000", "#5e0000", "#8a0000", "#b60000", "#e50000", "#f24d4d", "#f79999", "#fcdcdc"] },
|
||||
"focus-ring": { "role": "semantic", "displayName": "Blu Focus", "canonical": "#258cfb", "tonalRamp": ["#08213f", "#0c3a70", "#1257b6", "#258cfb", "#5aa9fc", "#8fc4fd", "#c4e0fe", "#eaf4ff"] }
|
||||
},
|
||||
"typographyMeta": {
|
||||
"page-title": { "displayName": "Page Title", "purpose": "Titolo di schermata; ancora l'utente al compito corrente." },
|
||||
"title": { "displayName": "Title", "purpose": "Intestazioni di sezione, titoli di dialog e message-box." },
|
||||
"body": { "displayName": "Body", "purpose": "Testo corrente e dati; prosa a 65-75ch." },
|
||||
"label": { "displayName": "Label", "purpose": "Etichette di pulsanti e controlli di form; peso 700 per presenza al tocco." }
|
||||
},
|
||||
"shadows": [
|
||||
{ "name": "ombra-custom", "value": "1px 2px 5px hsl(from var(--mud-palette-overlay-dark) h s 40%)", "purpose": "Stacco morbido per card ed elementi che galleggiano sul contenuto." },
|
||||
{ "name": "ombra-eccezione", "value": "1px 2px 5px rgba(0,0,0,0.3)", "purpose": "Rilievo leggermente più marcato per box di errore/eccezione." }
|
||||
],
|
||||
"motion": [
|
||||
{ "name": "connection-slide", "value": "all 0.5s ease", "purpose": "Comparsa/scomparsa della barra di stato connessione (translateY)." },
|
||||
{ "name": "ripple", "value": "ripple-effect 0.6s linear", "purpose": "Feedback fisico al tocco su pulsanti e contenitori tattili." },
|
||||
{ "name": "spinner", "value": "l24 1s infinite linear", "purpose": "Loader di caricamento a doppio anello." }
|
||||
],
|
||||
"breakpoints": [
|
||||
{ "name": "mobile", "value": "0px" }
|
||||
]
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"name": "Primary Button",
|
||||
"kind": "button",
|
||||
"refersTo": "button-primary",
|
||||
"description": "Azione primaria: fondo accento, etichetta bianca in grassetto, angoli molto arrotondati.",
|
||||
"html": "<button class=\"ds-btn-primary\">Salva</button>",
|
||||
"css": ".ds-btn-primary { font-family: Nunito, sans-serif; background: #ec4c41; color: #fff; font-weight: 700; font-size: 0.9375rem; border: none; border-radius: 20px; padding: 0.65rem 1.5rem; min-height: 44px; cursor: pointer; transition: filter 0.15s ease, transform 0.1s ease; } .ds-btn-primary:hover { filter: brightness(1.05); } .ds-btn-primary:focus-visible { outline: none; box-shadow: 0 0 0 0.1rem #fff, 0 0 0 0.25rem #258cfb; } .ds-btn-primary:active { transform: translateY(1px); filter: brightness(0.95); }"
|
||||
},
|
||||
{
|
||||
"name": "FAB (Floating Action)",
|
||||
"kind": "button",
|
||||
"refersTo": "button-primary",
|
||||
"description": "Azione primaria fissa in basso a destra, a portata di pollice; rispetta la safe-area.",
|
||||
"html": "<button class=\"ds-fab\" aria-label=\"Nuova scheda\"><svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M12 5v14M5 12h14\"/></svg></button>",
|
||||
"css": ".ds-fab { font-family: Nunito, sans-serif; position: fixed; bottom: 4rem; right: 16px; width: 56px; height: 56px; border-radius: 50%; border: none; background: #ec4c41; color: #fff; display: inline-flex; align-items: center; justify-content: center; box-shadow: 1px 2px 5px rgba(0,0,0,0.3); cursor: pointer; transition: filter 0.15s ease, transform 0.1s ease; } .ds-fab:hover { filter: brightness(1.05); } .ds-fab:active { transform: translateY(1px); } .ds-fab:focus-visible { outline: none; box-shadow: 0 0 0 0.1rem #fff, 0 0 0 0.25rem #258cfb; }"
|
||||
},
|
||||
{
|
||||
"name": "Input Card",
|
||||
"kind": "input",
|
||||
"refersTo": "input-card",
|
||||
"description": "Campo a scheda su fondo grigio tonale, senza underline; etichetta a sinistra, valore a destra.",
|
||||
"html": "<label class=\"ds-input-card\"><span class=\"ds-input-label\">Quantità</span><input class=\"ds-input-field\" type=\"text\" inputmode=\"numeric\" value=\"12\" /></label>",
|
||||
"css": ".ds-input-card { font-family: Nunito, sans-serif; display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; width: 100%; background: #f5f5f5; border-radius: 9px; padding: 0.5rem 1rem; min-height: 44px; } .ds-input-label { font-weight: 700; color: #000; } .ds-input-field { font-family: Nunito, sans-serif; font-weight: 500; text-align: right; border: none; background: transparent; color: #000; outline: none; min-width: 0; } .ds-input-field:focus { outline: none; }"
|
||||
},
|
||||
{
|
||||
"name": "Connection Status Bar",
|
||||
"kind": "custom",
|
||||
"refersTo": null,
|
||||
"description": "Striscia di stato rete/servizio che scorre dall'alto: verde se OK, rosso se KO. Incarna 'Offline è la verità'.",
|
||||
"html": "<div class=\"ds-connection ds-connection--ko\">Nessuna connessione — lavoro salvato in locale</div>",
|
||||
"css": ".ds-connection { font-family: Nunito, sans-serif; display: flex; align-items: center; gap: 1rem; min-height: 35px; padding: 0 0.75rem; font-weight: 700; font-size: 1.05rem; color: #fff; transition: all 0.5s ease; } .ds-connection--ko { background: #e50000; } .ds-connection--ok { background: #26b050; }"
|
||||
},
|
||||
{
|
||||
"name": "Panel / Card",
|
||||
"kind": "card",
|
||||
"refersTo": "panel",
|
||||
"description": "Superficie di contenuto: carta bianca, angoli 1em, piatta di default, ombra solo se galleggia.",
|
||||
"html": "<div class=\"ds-panel\"><h3 class=\"ds-panel-title\">Scheda articolo</h3><p class=\"ds-panel-body\">Dettagli della rilevazione.</p></div>",
|
||||
"css": ".ds-panel { font-family: Nunito, sans-serif; background: #fff; color: #000; border-radius: 1em; padding: 1rem; } .ds-panel-title { font-weight: 700; font-size: 1.1rem; margin: 0 0 0.5rem; } .ds-panel-body { font-weight: 400; font-size: 0.875rem; line-height: 1.8; margin: 0; }"
|
||||
}
|
||||
],
|
||||
"narrative": {
|
||||
"northStar": "Il taccuino da campo",
|
||||
"overview": "SteUP è lo strumento che il rilevatore tiene in mano mentre gira i punti vendita: deve funzionare come carta e inchiostro — sempre, anche senza rete, leggibile in pieno sole, comprensibile con un'occhiata. La chiarezza viene prima di qualsiasi ornamento. Il sistema è tattile e sicuro: bersagli ampi per il pollice, una sola mano e i guanti; feedback fisico immediato a ogni tocco e scansione. Lo stato — salvato in locale, in attesa di sync, inviato, completato — non è mai ambiguo. Rifiuta il gestionale datato e denso e l'app consumer social/giocosa: è uno strumento professionale.",
|
||||
"keyCharacteristics": [
|
||||
"Offline-first: lo stato di salvataggio e sincronizzazione è sempre esplicito",
|
||||
"Mobile-first, uso a una mano/guanti: tocchi ≥44px, azioni a portata di pollice",
|
||||
"Alto contrasto e testo generoso per la leggibilità all'aperto",
|
||||
"Un solo accento (Arancio-Rosso Allerta), usato per azione e stato, mai per decorazione",
|
||||
"Un'unica famiglia tipografica, angoli arrotondati coerenti (9px → 20px → 1em)"
|
||||
],
|
||||
"rules": [
|
||||
{ "name": "La Regola dell'Unico Accento", "body": "L'Arancio-Rosso Allerta è l'unico accento del sistema e appare solo su azione o stato attivo, mai come decorazione. La sua rarità è ciò che lo rende un segnale.", "section": "colors" },
|
||||
{ "name": "La Regola del Contrasto da Sole", "body": "Il testo di corpo è inchiostro (#000) su carta (#fff). Il grigio chiaro 'per eleganza' è vietato sul testo leggibile: si progetta per lo schermo colpito dal sole.", "section": "colors" },
|
||||
{ "name": "La Regola del Peso, non del Maiuscolo", "body": "L'enfasi si ottiene con il peso (700/800) e la dimensione, non con il maiuscolo spaziato. Niente eyebrow maiuscoli tracciati sopra le sezioni.", "section": "typography" },
|
||||
{ "name": "La Regola del Piatto-di-Default", "body": "Le superfici sono piatte a riposo. L'ombra compare solo quando un elemento deve galleggiare (overlay, dialog, card sollevata) o rispondere a uno stato.", "section": "elevation" }
|
||||
],
|
||||
"dos": [
|
||||
"Do usare l'Arancio-Rosso Allerta (#ec4c41) solo per azione e stato attivo; mantenerlo raro.",
|
||||
"Do tenere il testo di corpo in inchiostro (#000) su carta (#fff); alto contrasto per la lettura al sole.",
|
||||
"Do dimensionare i bersagli di tocco ≥44px e collocare le azioni primarie a portata di pollice (FAB in basso a destra).",
|
||||
"Do rendere sempre esplicito lo stato offline/sync (barra di connessione, stati di scheda/ispezione).",
|
||||
"Do dare feedback fisico immediato a ogni tocco e scansione (ripple, cambio di stato).",
|
||||
"Do usare un'unica famiglia (Nunito) e fare gerarchia con peso e dimensione.",
|
||||
"Do rispettare le safe-area iOS/Android e prefers-reduced-motion."
|
||||
],
|
||||
"donts": [
|
||||
"Don't costruire un gestionale datato e denso: niente tabelle fittissime, testo minuscolo, grigio ovunque.",
|
||||
"Don't virare verso un'estetica consumer social/giocosa: niente colore ludico o decorazione fine a sé stessa.",
|
||||
"Don't usare testo grigio chiaro sul corpo 'per eleganza': causa numero uno di illeggibilità al sole.",
|
||||
"Don't usare bordo-laterale colorato (border-left/right > 1px) come accento su card, liste o alert.",
|
||||
"Don't usare testo con gradiente, glassmorphism decorativo, griglie di card tutte identiche, o eyebrow maiuscoli su ogni sezione.",
|
||||
"Don't introdurre animazioni di scena orchestrate al caricamento: il movimento serve allo stato, non allo spettacolo.",
|
||||
"Don't reinventare affordance standard (scrollbar strane, controlli di form non convenzionali) per 'carattere'."
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## What this is
|
||||
|
||||
SteUP is an Integry .NET MAUI Blazor Hybrid app for field inspections ("ispezioni") of retail stores — the operator scans product barcodes (including via Honeywell handheld scanners), fills in inspection sheets ("schede"), attaches photos, and syncs with the Integry backend. Work is stored locally (offline-first via SQLite) and pushed to the server. The same Blazor UI also runs as a standalone WebAssembly app.
|
||||
|
||||
> Note: `README.md` is the leftover Microsoft sample template and does NOT describe this app. Ignore it.
|
||||
|
||||
## Design Context
|
||||
|
||||
For any UI/design work, read **`PRODUCT.md`** (strategy) and **`DESIGN.md`** (visual system) at the repo root first; they are the source of truth for the impeccable design skill.
|
||||
|
||||
- **Register:** product (a field tool; the UI serves the task, it is not a marketing surface).
|
||||
- **Users:** field inspectors ("rilevatori") in stores — one-handed / gloved, outdoors in sunlight, often offline.
|
||||
- **North Star:** "Il taccuino da campo" — works like paper and ink: always, offline, readable in full sun.
|
||||
- **Core principles:** offline state is never ambiguous · thumb-first (touch ≥44px) · high contrast for sunlight · immediate feedback per scan · fewer steps, fewer errors.
|
||||
- **Visual system:** MudBlazor · single font **Nunito** · accent `#ec4c41` (Arancio-Rosso Allerta, action/state only), anchor `#002339` (Blu Profondo) · rounded (9px/20px/1em) · locale `it-IT`.
|
||||
- **Anti-references:** no dated/dense back-office look; no playful consumer-social aesthetic.
|
||||
|
||||
## Build & run
|
||||
|
||||
`global.json` pins SDK 8.0.0 with `rollForward: latestMajor`, but all projects target **.NET 10** (MAUI targets `net10.0-android` / `net10.0-ios`). A .NET 10 SDK must be installed.
|
||||
|
||||
NuGet restores from a **private Integry feed only** (`NuGet.Config` clears nuget.org and uses `nuget.studioml.it`). The `IntegryApiClient.*` packages come from there.
|
||||
|
||||
```bash
|
||||
# MAUI app (primary target) — Android
|
||||
dotnet build SteUp.Maui/SteUp.Maui.csproj -t:Run -f net10.0-android
|
||||
|
||||
# MAUI app — iOS (requires Mac build host; codesigning wired in csproj)
|
||||
dotnet build SteUp.Maui/SteUp.Maui.csproj -f net10.0-ios
|
||||
|
||||
# Web (Blazor WASM) app
|
||||
dotnet run --project SteUp.Web/SteUp.Web.csproj
|
||||
```
|
||||
|
||||
There is **no test project** in the solution. There is no lint config beyond `Nullable`/`ImplicitUsings` enabled per csproj.
|
||||
|
||||
## Projects (5)
|
||||
|
||||
- **SteUp.Shared** — Razor Class Library holding almost everything: all Blazor pages/components, DTOs, entities, enums, the auth state provider, API service wrappers, messaging, and business orchestration (`SteupDataService`). Both MAUI and Web reference it. Uses MudBlazor + MudExtensions for UI.
|
||||
- **SteUp.Maui** — the native host. Owns platform services (DI registration in `Core/CoreModule.cs`), the `BlazorWebView` host (`MainPage.xaml.cs`), file/logging/network/scanner implementations, and app config.
|
||||
- **SteUp.Web** — Blazor WebAssembly host of the same shared UI. Its `Program.cs` mirrors a subset of the MAUI DI setup.
|
||||
- **SteUp.Data** — EF Core + SQLite local database (`AppDbContext`, migrations, `IspezioniService`). References Shared for the entity types.
|
||||
- **Steup.HoneywellScanner** — Android-only binding library wrapping Honeywell `DataCollection.aar`. Referenced by MAUI only on the Android target.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Interface / implementation split (important convention)
|
||||
Interfaces live in **SteUp.Shared** (`Core/Interface/...`) so the shared UI depends only on abstractions. Concrete platform implementations live in **SteUp.Maui** (`Core/Services`, `Core/System`) and are registered per-host:
|
||||
- MAUI wires everything in `SteUp.Maui/Core/CoreModule.cs` via `Register*` extension methods called from `MauiProgram.cs`.
|
||||
- Web wires a smaller subset directly in `SteUp.Web/Program.cs` (only `IFormFactor` + auth; it has no local DB, scanner, or file manager).
|
||||
|
||||
When adding a service consumed by the shared UI: define the interface in `SteUp.Shared/Core/Interface`, implement it in the host, and register it in **both** `CoreModule.cs` and (if the Web app needs it) `Program.cs`.
|
||||
|
||||
### Auth
|
||||
`AppAuthenticationStateProvider` (Shared) bridges Integry's `IUserSession`/`IUserAccountService` (from `IntegryApiClient`) to Blazor's `AuthenticationStateProvider`, building `ClaimsPrincipal` from the JWT. Both hosts call `builder.UseIntegry(appToken, useLoginAzienda: true)` — the `AppToken` is the same GUID in `MauiProgram.cs` and `SteUp.Web/Program.cs`.
|
||||
|
||||
### Backend calls
|
||||
Two service wrappers over `IIntegryApiRestClient`:
|
||||
- `IntegrySteupService` — the app-specific `steup/*` endpoints (retrieve punti vendita, reparti, griglia PLU, barcode lookup, save/complete/delete schede, upload attachments).
|
||||
- `IntegryApiService` — generic Integry endpoints (system health, activity descriptions, email).
|
||||
|
||||
### Offline-first data flow
|
||||
`SteupDataService` (Shared) is the orchestrator: on `Init()` it runs EF migrations (`DbInitializer.InitializeAsync` → `db.Database.MigrateAsync()`), loads reference data from the API, reconciles inspection status, and prunes closed inspections older than 60 days. Local reads/writes go through `IspezioniService` (SteUp.Data).
|
||||
|
||||
### Local DB model
|
||||
`AppDbContext`: `Ispezione` → `Scheda` → `SchedaArticolo`. `Ispezione` has a **composite key** `(CodMdep, Data, Rilevatore)` which is also the FK from `Scheda`. `Scheda.ImageNames` is a `List<string>` persisted as JSON via a value converter. DB file: `steup_db.db3` in `FileSystem.AppDataDirectory` (see `DbPathProvider`).
|
||||
|
||||
Add a migration (from repo root; `AppDbContextFactory` provides the design-time context using a throwaway `design-time.db3`):
|
||||
```bash
|
||||
dotnet ef migrations add <Name> --project SteUp.Data
|
||||
```
|
||||
Migrations are applied automatically at app startup, not via `database update`.
|
||||
|
||||
### Messaging (in-app events)
|
||||
Uses `CommunityToolkit.Mvvm` `WeakReferenceMessenger`. Singleton "services" in `Shared/Core/Messages` (`NewSchedaService`, `OnScannerService`, `CompleteInspectionService`) publish/relay messages that decouple the barcode scanner and page components.
|
||||
|
||||
### Barcode scanning
|
||||
`IBarcodeReaderService` is implemented by `HoneywellScannerService` (MAUI, partial class with Android-specific parts + the Honeywell binding lib). `IBarcodeManager`/`BarcodeManager` (Shared) is the host-agnostic front the UI talks to. Scans flow to the UI through the messaging services above.
|
||||
|
||||
### Attached images
|
||||
Photos are served into the `BlazorWebView` through a custom scheme: `MainPage.xaml.cs` intercepts `WebResourceRequested` for `https://localfiles/attached/<file>` and streams files from `CacheDirectory/attached` (with path-traversal guards).
|
||||
|
||||
## Error tracking
|
||||
Sentry is configured in both hosts (`.UseSentry(...)` in MAUI, Sentry package in Shared). MAUI also has a `GlobalExceptionHandler` (registered in `App.xaml.cs`) and a `FileLogger`/`FileLoggerProvider` writing to `AppDataDirectory/logs`.
|
||||
|
||||
## Versioning
|
||||
App version lives in `SteUp.Maui.csproj` (`ApplicationDisplayVersion` + `ApplicationVersion`). Commit messages track releases (e.g. "-> v1.0.2 (3)"). Bump both when releasing. `ApplicationId` is `it.integry.SteUp`.
|
||||
@@ -0,0 +1,192 @@
|
||||
---
|
||||
name: SteUP
|
||||
description: Strumento da campo per ispezioni nei punti vendita — solido, chiaro, offline-first.
|
||||
colors:
|
||||
primary: "#ec4c41"
|
||||
secondary: "#002339"
|
||||
tertiary: "#dff2ff"
|
||||
ink: "#000000"
|
||||
paper: "#ffffff"
|
||||
surface-dark: "#000406"
|
||||
success: "#26b050"
|
||||
error: "#e50000"
|
||||
focus-ring: "#258cfb"
|
||||
typography:
|
||||
page-title:
|
||||
fontFamily: "Nunito, sans-serif"
|
||||
fontSize: "1.5rem"
|
||||
fontWeight: 800
|
||||
lineHeight: 1.2
|
||||
letterSpacing: "normal"
|
||||
title:
|
||||
fontFamily: "Nunito, sans-serif"
|
||||
fontSize: "1.1rem"
|
||||
fontWeight: 700
|
||||
lineHeight: 1.4
|
||||
letterSpacing: "normal"
|
||||
body:
|
||||
fontFamily: "Nunito, sans-serif"
|
||||
fontSize: "0.875rem"
|
||||
fontWeight: 400
|
||||
lineHeight: 1.8
|
||||
letterSpacing: "normal"
|
||||
label:
|
||||
fontFamily: "Nunito, sans-serif"
|
||||
fontSize: "0.875rem"
|
||||
fontWeight: 700
|
||||
lineHeight: 1.4
|
||||
letterSpacing: "normal"
|
||||
rounded:
|
||||
input: "9px"
|
||||
panel: "1em"
|
||||
container: "20px"
|
||||
skeleton: "0.5em"
|
||||
spacing:
|
||||
page-x: "1rem"
|
||||
header-h: "4rem"
|
||||
touch-min: "44px"
|
||||
components:
|
||||
button-primary:
|
||||
backgroundColor: "{colors.primary}"
|
||||
textColor: "{colors.paper}"
|
||||
rounded: "{rounded.container}"
|
||||
typography: "{typography.label}"
|
||||
padding: "0.5rem 1.25rem"
|
||||
button-primary-active:
|
||||
backgroundColor: "{colors.primary}"
|
||||
textColor: "{colors.paper}"
|
||||
rounded: "{rounded.container}"
|
||||
input-card:
|
||||
backgroundColor: "#f5f5f5"
|
||||
textColor: "{colors.ink}"
|
||||
rounded: "{rounded.input}"
|
||||
padding: "0.5rem 1rem"
|
||||
panel:
|
||||
backgroundColor: "{colors.paper}"
|
||||
textColor: "{colors.ink}"
|
||||
rounded: "{rounded.panel}"
|
||||
padding: "1rem"
|
||||
---
|
||||
|
||||
# Design System: SteUP
|
||||
|
||||
## 1. Overview
|
||||
|
||||
**Creative North Star: "Il taccuino da campo"**
|
||||
|
||||
SteUP è lo strumento che il rilevatore tiene in mano mentre gira i punti vendita: deve funzionare come carta e inchiostro — sempre, anche senza rete, leggibile in pieno sole, comprensibile con un'occhiata. La chiarezza viene prima di qualsiasi ornamento. L'interfaccia poggia su MudBlazor con un'unica famiglia tipografica (Nunito) e una palette essenziale: un solo accento caldo per l'azione, un blu profondo come base stabile, superfici bianche e pulite con angoli generosamente arrotondati.
|
||||
|
||||
Il sistema è **tattile e sicuro**: bersagli ampi pensati per il pollice, una sola mano e i guanti; feedback fisico immediato a ogni tocco e a ogni scansione (effetto ripple, cambi di stato evidenti). Lo stato — salvato in locale, in attesa di sync, inviato al server, completato — non è mai ambiguo: è la funzione più importante che la UI comunica.
|
||||
|
||||
Questo sistema rifiuta esplicitamente due cose. Non è un **gestionale datato e denso**: niente tabelle fittissime, testo minuscolo, grigio ovunque, densità che non serve al lavoro da campo. E non è un'**app consumer social/giocosa**: niente colore ludico, niente decorazione fine a sé stessa. È uno strumento professionale.
|
||||
|
||||
**Key Characteristics:**
|
||||
- Offline-first: lo stato di salvataggio e sincronizzazione è sempre esplicito
|
||||
- Mobile-first, uso a una mano/guanti: tocchi ≥44px, azioni a portata di pollice
|
||||
- Alto contrasto e testo generoso per la leggibilità all'aperto
|
||||
- Un solo accento (Arancio-Rosso Allerta), usato per azione e stato, mai per decorazione
|
||||
- Un'unica famiglia tipografica, angoli arrotondati coerenti (9px → 20px → 1em)
|
||||
|
||||
## 2. Colors
|
||||
|
||||
Una palette essenziale: un accento caldo che chiama l'azione, un blu profondo come ancora, il resto neutro e pulito.
|
||||
|
||||
### Primary
|
||||
- **Arancio-Rosso Allerta** (#ec4c41): il colore dell'azione e dello stato attivo. Pulsanti primari, FAB, selezione corrente, icone d'azione, indicatori di stato che richiedono attenzione. Riservato: la sua presenza segnala "qui si agisce".
|
||||
|
||||
### Secondary
|
||||
- **Blu Profondo** (#002339): base stabile e di struttura. Testo forte, intestazioni, elementi strutturali, superfici scure in dark mode. È l'ancora visiva contro cui l'accento risalta.
|
||||
|
||||
### Tertiary
|
||||
- **Azzurro Tenue** (#dff2ff): tocco freddo e leggero per evidenziazioni informative e superfici secondarie, dove serve distinguere senza gridare.
|
||||
|
||||
### Neutral
|
||||
- **Inchiostro** (#000000): corpo del testo su fondo chiaro; massimo contrasto per la lettura al sole.
|
||||
- **Carta** (#ffffff): superficie di contenuto primaria (pannelli, card, dialog).
|
||||
- **Superficie Scura** (#000406): sfondo della dark mode (attualmente predisposta ma disattivata).
|
||||
- **Grigio Campo** (~#f5f5f5): fondo delle input-card e delle superfici tonali secondarie; distingue per tono, non per bordo.
|
||||
|
||||
### Semantic
|
||||
- **Verde OK** (#26b050): validazione positiva, esito riuscito.
|
||||
- **Rosso Errore** (#e50000): validazione fallita, messaggi d'errore, connessione/servizio KO.
|
||||
- **Blu Focus** (#258cfb): anello di focus per la navigazione da tastiera.
|
||||
|
||||
### Named Rules
|
||||
**La Regola dell'Unico Accento.** L'Arancio-Rosso Allerta è l'unico accento del sistema e appare solo su azione o stato attivo, mai come decorazione. La sua rarità è ciò che lo rende un segnale.
|
||||
|
||||
**La Regola del Contrasto da Sole.** Il testo di corpo è inchiostro (#000) su carta (#fff). Il grigio chiaro "per eleganza" è vietato sul testo leggibile: si progetta per lo schermo colpito dal sole, non per lo screenshot.
|
||||
|
||||
## 3. Typography
|
||||
|
||||
**Display / Body / Label Font:** Nunito (variable 200–1000, con fallback `sans-serif`). Caricata da Google Fonts; usata su tutta l'interfaccia.
|
||||
|
||||
**Character:** un'unica famiglia sans humanist e morbida per tutto — titoli, etichette, pulsanti, corpo, dati. Gli angoli tondeggianti di Nunito si accordano con i raggi ampi dei componenti e rafforzano il tono solido ma accessibile del "taccuino da campo". Nessun accostamento display/body: la gerarchia si fa con peso e dimensione.
|
||||
|
||||
### Hierarchy
|
||||
- **Page Title** (peso 800, ~1.5rem/x-large, line-height stretta): titolo di schermata; ancora l'utente al compito corrente.
|
||||
- **Title** (peso 700, ~1.1rem): intestazioni di sezione, titoli di dialog e message-box.
|
||||
- **Body** (peso 400, 0.875rem/14px, line-height 1.8): testo corrente e dati. Per la prosa mantenere 65–75ch; dati e UI compatta possono essere più densi.
|
||||
- **Label** (peso 700, 0.875rem): etichette dei pulsanti e dei controlli di form. Il grassetto dà presenza al tocco senza ricorrere al maiuscolo.
|
||||
|
||||
### Named Rules
|
||||
**La Regola del Peso, non del Maiuscolo.** L'enfasi si ottiene con il peso (700/800) e la dimensione, non con il maiuscolo spaziato. Niente eyebrow maiuscoli tracciati sopra le sezioni.
|
||||
|
||||
## 4. Elevation
|
||||
|
||||
Sistema prevalentemente **piatto con ombre funzionali leggere**. La profondità è tonale (Carta su Grigio Campo) più un'ombra morbida e diffusa dove un elemento deve staccarsi davvero (card in rilievo, overlay). MudBlazor fornisce la scala di elevazione; sopra di essa il progetto definisce due ombre custom soffuse. Le superfici a riposo sono piatte; l'ombra è una risposta al ruolo (galleggiamento, overlay), non decorazione.
|
||||
|
||||
### Shadow Vocabulary
|
||||
- **Ombra Custom** (`box-shadow: 1px 2px 5px hsl(from var(--mud-palette-overlay-dark) h s 40%)`): stacco morbido per card ed elementi che devono galleggiare sul contenuto.
|
||||
- **Ombra Eccezione** (`box-shadow: 1px 2px 5px rgba(0,0,0,0.3)`): rilievo leggermente più marcato per box di errore/eccezione.
|
||||
|
||||
### Named Rules
|
||||
**La Regola del Piatto-di-Default.** Le superfici sono piatte a riposo. L'ombra compare solo quando un elemento deve galleggiare (overlay, dialog, card sollevata) o rispondere a uno stato. Se sembra un'app del 2014, l'ombra è troppo scura e troppo stretta.
|
||||
|
||||
## 5. Components
|
||||
|
||||
Componenti **tattili e sicuri**: bersagli generosi e arrotondati, feedback fisico, presenza netta, pensati per il pollice in campo.
|
||||
|
||||
### Buttons
|
||||
- **Shape:** angoli molto arrotondati (raggio 20px sul contenitore `container-button`; i pulsanti MudBlazor ereditano `--mud-default-borderradius: 20px`).
|
||||
- **Primary:** fondo Arancio-Rosso Allerta (#ec4c41), testo Carta (#fff), etichetta in peso 700; padding compatto verticale ma area di tocco ampia.
|
||||
- **Hover / Focus / Active:** effetto **ripple** al tocco (chiaro su fondo scuro, scuro su fondo chiaro); focus da tastiera con doppio anello (bianco + Blu Focus #258cfb). Nei contenitori `ripple-container` il ripple nativo Mud è disattivato in favore di quello custom.
|
||||
- **Settings buttons:** riga con icona in "pill" arrotondata (raggio 6px) tinta per ruolo — grigio (neutro), primary (azione), verde (successo), rosso (distruttivo) — su fondo tenue della stessa tinta.
|
||||
- **FAB (`custom-mudfab`):** azione primaria fissa in basso a destra (bottom 4rem, right 16px), a portata di pollice; rispetta la safe-area inferiore.
|
||||
|
||||
### Cards / Containers
|
||||
- **Corner Style:** pannelli e dialog a 1em; contenitori d'azione a 20px; input-card a 9px.
|
||||
- **Background:** Carta (#fff) per il contenuto; Grigio Campo (~#f5f5f5) per input-card e superfici tonali secondarie.
|
||||
- **Shadow Strategy:** piatte di default; Ombra Custom solo quando devono galleggiare (vedi Elevation).
|
||||
- **Border:** preferire lo stacco tonale al bordo; quando serve, bordo pieno sottile (`--card-border-color`). Mai bordo-laterale colorato come accento.
|
||||
- **Internal Padding:** input-card `.5rem 1rem`; pannelli ~1rem; margine orizzontale di pagina `--m-page-x: 1rem`.
|
||||
|
||||
### Inputs / Fields
|
||||
- **Style:** input dentro `input-card` su fondo Grigio Campo, raggio 9px; underline Mud rimossa (`:before/:after` a `none`) per un aspetto pulito a "scheda".
|
||||
- **Layout:** `form-container` a due colonne (etichetta in peso 700 a sinistra, valore a destra), altezza minima 35px per un tocco comodo.
|
||||
- **Valid / Error:** valido → outline verde 1px (#26b050); invalido → outline rosso 1px (#e50000) con `validation-message` in rosso.
|
||||
|
||||
### Navigation
|
||||
- **NavMenu** in testa alla pagina; barra di stato connessione (`ConnectionState`) che scorre dall'alto: verde (SystemOk) o rosso (NetworkKo / ServicesIsDown), peso 700, testo bianco. Comunica lo stato di rete/servizio senza rubare spazio al contenuto.
|
||||
|
||||
### Signature: Barra di Stato Connessione
|
||||
Striscia sottile animata (`.Connection`) ancorata in cima che appare/scompare con transizione morbida (translateY) per segnalare rete assente o backend giù. È l'incarnazione visiva del principio "Offline è la verità": lo stato di connettività è sempre onesto e visibile.
|
||||
|
||||
## 6. Do's and Don'ts
|
||||
|
||||
### Do:
|
||||
- **Do** usare l'Arancio-Rosso Allerta (#ec4c41) solo per azione e stato attivo; mantenerlo raro (Regola dell'Unico Accento).
|
||||
- **Do** tenere il testo di corpo in inchiostro (#000) su carta (#fff); alto contrasto per la lettura al sole.
|
||||
- **Do** dimensionare i bersagli di tocco ≥44px e collocare le azioni primarie a portata di pollice (FAB in basso a destra), pensando all'uso a una mano/guanti.
|
||||
- **Do** rendere sempre esplicito lo stato offline/sync (barra di connessione, stati di scheda/ispezione).
|
||||
- **Do** dare feedback fisico immediato a ogni tocco e scansione (ripple, cambio di stato).
|
||||
- **Do** usare un'unica famiglia (Nunito) e fare gerarchia con peso e dimensione.
|
||||
- **Do** rispettare le safe-area iOS/Android e `prefers-reduced-motion`.
|
||||
|
||||
### Don't:
|
||||
- **Don't** costruire un **gestionale datato e denso**: niente tabelle fittissime, testo minuscolo, form infiniti, grigio ovunque.
|
||||
- **Don't** virare verso un'estetica **consumer social/giocosa**: niente colore ludico o decorazione fine a sé stessa.
|
||||
- **Don't** usare testo grigio chiaro sul corpo "per eleganza": è la causa numero uno di illeggibilità al sole.
|
||||
- **Don't** usare bordo-laterale colorato (`border-left/right` > 1px) come accento su card, liste o alert.
|
||||
- **Don't** usare testo con gradiente (`background-clip: text`), glassmorphism decorativo, griglie di card tutte identiche, o eyebrow maiuscoli tracciati su ogni sezione.
|
||||
- **Don't** introdurre animazioni di scena orchestrate al caricamento: l'app entra dritta nel compito; il movimento serve allo stato, non allo spettacolo.
|
||||
- **Don't** reinventare affordance standard (scrollbar strane, controlli di form non convenzionali) per "carattere".
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
# Product
|
||||
|
||||
## Register
|
||||
|
||||
product
|
||||
|
||||
## Users
|
||||
|
||||
Utente primario: **il rilevatore in campo**. Gira i punti vendita con un telefono o uno scanner Honeywell in mano, spesso in piedi, in movimento, all'aperto con luce forte, a volte con una mano sola o con i guanti, e con connessione di rete incerta o assente. Il suo compito durante il turno: scansionare i barcode degli articoli, compilare le schede di ispezione, allegare foto e chiudere l'ispezione. Strumento a uso interno Integry (non rivolto a clienti esterni), quindi contano più l'efficienza e la fiducia operativa che l'identità di marca.
|
||||
|
||||
## Product Purpose
|
||||
|
||||
SteUP è lo strumento da campo per le ispezioni dei punti vendita. Esiste per far completare le rilevazioni in modo rapido, accurato e senza perdita di dati, anche quando manca la rete. Il lavoro viene salvato localmente (offline-first, SQLite sul dispositivo) e sincronizzato con il backend Integry quando la connessione torna. Successo = il rilevatore finisce il turno certo che ogni scheda sia stata registrata e inviata, senza mai dover ripetere il lavoro per un dato perso o un dubbio sullo stato.
|
||||
|
||||
## Brand Personality
|
||||
|
||||
Solida, chiara, senza fronzoli. È uno strumento da lavoro, non una vetrina: nitido, diretto, prevedibile. La fiducia nasce dalla robustezza percepita e dalla leggibilità dello stato, non dalla decorazione. Tono asciutto e concreto nei testi (etichette, conferme, errori). Coerente con l'identità esistente: corallo (`#ec4c41`) come colore d'azione, navy (`#002339`) come ancora, superfici pulite e arrotondate.
|
||||
|
||||
## Anti-references
|
||||
|
||||
- **Gestionale datato/denso.** Niente look da ERP: tabelle fittissime, testo minuscolo, form infiniti, grigio ovunque, densità che non serve al compito da campo.
|
||||
- **Consumer social/giocoso.** Niente estetica colorata/ludica da app social. Resta uno strumento professionale.
|
||||
- Valgono inoltre i divieti trasversali di impeccable: niente gradienti decorativi, niente testo con gradiente, niente glassmorphism di default, niente griglie di card tutte identiche, niente eyebrow maiuscoli su ogni sezione.
|
||||
|
||||
## Design Principles
|
||||
|
||||
- **Offline è la verità.** Lo stato di salvataggio e sincronizzazione non è mai ambiguo. Il rilevatore deve sempre poter capire, senza pensarci, cosa è salvato in locale e cosa è arrivato al server.
|
||||
- **Il pollice comanda.** Progettato per l'uso a una mano, con i guanti, in movimento: target di tocco ampi (≥44px), azioni primarie a portata di pollice, poco affollamento, niente bersagli piccoli.
|
||||
- **Leggibile al sole.** Contrasto alto e testo generoso come default, non come opzione. La leggibilità all'aperto viene prima dell'eleganza del grigio chiaro.
|
||||
- **Feedback immediato a ogni scan.** Ogni scansione e ogni tocco producono una risposta percepibile e istantanea; l'app conferma, non fa attendere.
|
||||
- **Meno passaggi, meno errori.** Il percorso più breve per completare una scheda corretta. Ogni schermata ha un compito primario evidente; le conferme prevengono l'errore invece di segnalarlo dopo.
|
||||
|
||||
## Accessibility & Inclusion
|
||||
|
||||
- **Uso all'aperto:** alto contrasto e dimensioni testo generose per la luce diretta del sole; evitare testo grigio chiaro su fondi tenui.
|
||||
- **Uso a una mano / con guanti:** target di tocco ≥44px, spaziatura sufficiente tra i controlli, azioni chiave raggiungibili in basso.
|
||||
- **Base WCAG AA come buona pratica:** corpo testo ≥4.5:1, focus visibili, rispetto di `prefers-reduced-motion`.
|
||||
- Interfaccia in **italiano** (locale `it-IT`).
|
||||
Reference in New Issue
Block a user