La Dashboard Lemmario AtLiTeG è un'applicazione web Single Page Application (SPA) costruita con React, TypeScript e Vite. Utilizza un'architettura component-based con gestione dello stato centralizzata tramite React Context API.
- React 18.3: Libreria UI con hooks moderni
- TypeScript 5.0: Type safety e migliore developer experience
- Vite 6.3: Build tool veloce con HMR
- Tailwind CSS 3.4: Utility-first CSS framework
- Radix UI: Componenti headless accessibili (Select, Dialog, Tooltip, Popover, etc.)
- Lucide React: Libreria icone moderna
- Leaflet 1.9: Libreria mappa interattiva
- React Leaflet 4.2: React bindings per Leaflet
- Leaflet.markercluster: Plugin per clustering marker
- PapaParse 5.5: Parser CSV performante
- GeoJSON: Standard per dati geografici
┌─────────────────────────────────────────────────────────────┐
│ App Component │
│ (AppProvider wrap) │
└─────────────────────────────────────────────────────────────┘
│
┌─────────┴─────────┐
│ │
┌───────▼──────┐ ┌──────▼─────────┐
│ Context │ │ Data Loading │
│ (Global │ │ (useDataLoader)│
│ State) │ │ │
└───────┬──────┘ └──────┬─────────┘
│ │
┌───────────┴───────────────────┴───────────┐
│ │
┌────▼────┐ ┌──────▼──────┐
│ Filters │ │ Filtered │
│ (Multi- │ │ Data │
│ Select) │ │ (useFiltered│
└────┬────┘ │ Data) │
│ └──────┬──────┘
│ │
└──────────────┬───────────────────────────┘
│
┌──────────────┴──────────────┐
│ │
┌───────▼────────┐ ┌───────▼────────┐
│ Components │ │ Components │
│ - Header │ │ - Timeline │
│ - SearchBar │ │ - Index │
│ - Map │ │ - Details │
│ - Metrics │ │ │
└────────────────┘ └────────────────┘
interface Lemma {
IdLemma: number; // ID univoco
Lemma: string; // Forma base normalizzata
Forma: string; // Variante attestata
CollGeografica: string; // Località
Anno: number; // Anno attestazione
Periodo: string; // Periodo temporale
Categoria: string; // Categorie multiple (CSV)
Frequenza: number; // Frequenza attestazione
URL: string; // Link risorsa esterna
IdAmbito?: string; // ID area geografica GeoJSON
lat?: number; // Latitudine (se puntuale)
lng?: number; // Longitudine (se puntuale)
}interface FilterState {
categorie: string[]; // Filtro categorie (multi)
periodi: string[]; // Filtro periodi (multi)
searchQuery: string; // Query ricerca
selectedLetter: string | null; // Lettera alfabeto
selectedYear: number | null; // Anno timeline
selectedLemma: Lemma | null; // Lemma selezionato
}AppContext
├── filters: FilterState
├── setCategorie(categorie: string[])
├── setPeriodi(periodi: string[])
├── setSearchQuery(query: string)
├── setSelectedLetter(letter: string | null)
├── setSelectedYear(year: number | null)
├── setSelectedLemma(lemma: Lemma | null)
└── resetFilters()-
Caricamento Dati (
useDataLoader)- Fetch CSV da
/data/Lemmi_forme_atliteg_updated.csv - Fetch GeoJSON da
/data/Ambiti geolinguistici newline.json - Parsing con PapaParse e normalizzazione
- Mapping coordinate per aree geografiche (IdAmbito)
- Fetch CSV da
-
Filtraggio Dati (
useFilteredData)- Input: lemmas completi + FilterState
- Filtra per categorie selezionate (multi-match)
- Filtra per periodi selezionati
- Filtra per query ricerca (Lemma o Forma)
- Filtra per lettera iniziale
- Filtra per anno selezionato
- Output: lemmas filtrati
-
Calcolo Metriche (
useMetrics)- Input: lemmas filtrati
- Calcola: unique località, unique lemmi, anni con attestazioni, totale attestazioni
- Output: oggetto metriche
Tutti i componenti si aggiornano in tempo reale quando cambia lo stato globale:
Filtri cambiati → Context aggiornato → useFilteredData ricalcola
→ Tutti i componenti che usano filteredLemmas si re-renderizzano
- Responsabilità: Branding e informazioni progetto
- Props: Nessuna
- Stato: Stateless
- Responsabilità: Filtri globali categoria e periodo
- Props: availableCategories, availablePeriods, selectedCategories, selectedPeriods, callbacks
- Stato: Controllato da Context
- Features: Multi-select, badge attivi, reset
- Responsabilità: Ricerca autocompletante
- Props: lemmas, onSelect
- Stato: Locale per query, debounce 300ms
- Features: Suggerimenti, navigazione tastiera
- Responsabilità: Visualizzazione mappa interattiva
- Props: lemmas (filtrati), selectedLemma, onLemmaSelect
- Stato: Locale per istanza mappa Leaflet
- Features:
- Marker per località puntuali
- Poligoni per aree geografiche (IdAmbito)
- Popup con dettagli
- Clustering per performance
- Conteggio località/lemmi
- Responsabilità: Navigazione temporale
- Props: lemmas (filtrati), selectedLemma, onLemmaSelect
- Stato: Locale per scroll, Context per selectedYear
- Features:
- Range anni dinamico dal dataset
- Click anno filtra dashboard
- Tooltip con dettagli
- Scroll orizzontale con frecce
- Evidenziazione anni con/senza attestazioni
- Responsabilità: Indice alfabetico navigabile
- Props: lemmas (filtrati), onLemmaSelect
- Stato: Context per selectedLetter
- Features:
- Lettere cliccabili con conteggio
- Filtra per lettera iniziale
- Raggruppamento lemmi con forme
- Scroll virtualizzato
- Responsabilità: Pannello dettagli lemma
- Props: lemma (selezionato), allLemmas (filtrati)
- Stato: Stateless
- Features:
- Stato vuoto quando nessuna selezione
- Categorie multiple come badge
- Tutte le attestazioni ordinate
- Link esterno
- Scroll area
- Responsabilità: Visualizzazione metriche
- Props: metrics (calcolate)
- Stato: Stateless
- Features:
- Conteggi località, lemmi, anni, attestazioni
- Icone informative
- Aggiornamento real-time
function useDataLoader(): {
lemmas: Lemma[];
geoAreas: GeoArea[];
isLoading: boolean;
error: string | null;
}- Carica dati CSV e GeoJSON
- Gestisce stato loading/error
- Memoizza risultati
function useFilteredData(
lemmas: Lemma[],
filters: FilterState
): Lemma[]- Applica tutti i filtri in sequenza
- Memoizza risultato per performance
- Supporta filtri multipli combinati
function useMetrics(lemmas: Lemma[]): {
localitaCount: number;
lemmiCount: number;
anniCount: number;
attestazioniCount: number;
}- Calcola metriche da lemmi filtrati
- Memoizza per evitare ricalcoli
function useDebounce<T>(value: T, delay: number): T- Debouncing per ricerca (300ms)
- Previene chiamate eccessive
loadLemmasCSV(path: string): Promise<Lemma[]>
loadGeoAreasJSON(path: string): Promise<GeoArea[]>
parseCategories(categoriaString: string): string[]
extractUniqueCategories(lemmas: Lemma[]): string[]
extractUniquePeriods(lemmas: Lemma[]): string[]- Parsing categorie multiple da CSV
- Verifica appartenenza categoria
- Normalizzazione nomi categorie
- Normalizzazione dati lemmi
- Mapping lemmi → GeoAreas (IdAmbito)
- Calcolo centroide poligoni
- Raggruppamento per località/anno
- Estrazione range anni
- Validazione lemmi e GeoAreas
- Validazione coordinate
- Validazione formati CSV/GeoJSON
- React.memo: Componenti pesanti (Map, Timeline)
- useMemo: Calcoli costosi (filteredData, metrics, grouping)
- useCallback: Funzioni passate come props
- Code Splitting: React.lazy per componenti pesanti
- Debouncing: Ricerca con 300ms delay
- Virtualizzazione: react-window per liste lunghe (Index)
- Marker Clustering: Leaflet.markercluster per mappa
- Tree Shaking: Vite automatico
- Minification: Production build
- Gzip: Nginx compression
- Code Splitting: Dynamic imports
- Target: < 500KB gzipped
- Attuale: ~155KB gzipped (✓)
- Navigazione tastiera: Tab, Shift+Tab, Enter, Escape, Frecce
- ARIA labels: Su tutti i componenti interattivi
- ARIA roles: List, listitem, region, etc.
- Focus visibile: Outline personalizzato
- Contrasto colori: 4.5:1 per testo, 3:1 per UI
- Screen reader: Annunci con aria-live regions
- Skip links: Navigazione rapida (planned)
- axe DevTools
- Lighthouse (target: 95+)
- Test manuali con screen reader
Stage 1: Builder (Node 20)
- npm ci --legacy-peer-deps
- npm run build
Stage 2: Production (Nginx Alpine)
- Copia build artifacts
- Copia dati CSV/GeoJSON
- Espone porta 9000
- Health check- Porta: 9000
- Gzip compression
- Cache headers per static assets
- SPA fallback routing
- Security headers
volumes:
- ./data:/usr/share/nginx/html/data:roPermette aggiornamento dati senza rebuild
npm run dev # Vite dev server su porta 5173npm run build # Build produzione
npm run build:prod # Build ottimizzata
npm run preview # Preview build localedocker-compose up -d # Build & run
docker-compose logs -f # View logs
docker-compose down # Stop
docker-compose up -d --build # Rebuild- Custom hooks (useFilteredData, useMetrics, useDebounce)
- Utils (categoryParser, dataTransformers, validators)
- Services (dataLoader)
- Filters, SearchBar, Header (props/stati diversi)
- Filtraggio completo
- Ricerca → selezione → dettaglio
- Timeline → click anno → sincronizzazione
- Index → click lettera → filtro
- Reset filtri
- Bundle size < 500KB
- LCP < 2.5s
- FID < 100ms
- CLS < 0.1
- Export dati filtrati (CSV/JSON)
- Condivisione URL con filtri (query params)
- Visualizzazioni aggiuntive (grafici, statistiche)
- Confronto periodi/località
- Backend API per dataset grandi
- PWA capabilities (offline, caching)
- i18n (multilingua)
- Dark mode
- Virtual scrolling per tutte le liste
- Web Workers per parsing CSV
- Service Worker per caching dati