#!/bin/bash # Script per generare tutti i file mancanti dell'app ASE Monitor # Questo script crea automaticamente tutte le schermate e widget set -e echo "========================================" echo " ASE Monitor - Generatore File App" echo "========================================" echo "" cd "$(dirname "$0")" # Colori per output GREEN='\033[0;32m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Verifica che siamo nella directory giusta if [ ! -f "pubspec.yaml" ]; then echo "Errore: esegui questo script dalla root del progetto Flutter" exit 1 fi echo -e "${BLUE}Creando struttura directory...${NC}" mkdir -p lib/screens mkdir -p lib/widgets # Conta file esistenti EXISTING=$(find lib -name "*.dart" | wc -l) echo -e "${GREEN}✓${NC} File Dart esistenti: $EXISTING" echo "" echo -e "${BLUE}Generando file mancanti...${NC}" echo "" # =========================================== # SCREENS # =========================================== # --- Login Screen --- echo "Creando lib/screens/login_screen.dart..." cat > lib/screens/login_screen.dart << 'DART_EOF' import 'package:flutter/material.dart'; import '../utils/constants.dart'; import '../services/auth_service.dart'; import 'home_screen.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State { final _formKey = GlobalKey(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); final _authService = AuthService(); bool _isLoading = false; bool _obscurePassword = true; String? _errorMessage; @override void dispose() { _emailController.dispose(); _passwordController.dispose(); super.dispose(); } Future _handleLogin() async { if (!_formKey.currentState!.validate()) return; setState(() { _isLoading = true; _errorMessage = null; }); try { final success = await _authService.login( _emailController.text.trim(), _passwordController.text, ); if (success && mounted) { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (context) => const HomeScreen()), ); } else { setState(() { _errorMessage = 'Email o password non corretti'; }); } } catch (e) { setState(() { _errorMessage = 'Errore durante il login'; }); } finally { if (mounted) { setState(() { _isLoading = false; }); } } } @override Widget build(BuildContext context) { return Scaffold( body: Container( decoration: const BoxDecoration( gradient: AppColors.primaryGradient, ), child: SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(AppSizes.paddingL), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildHeader(), const SizedBox(height: 60), _buildLoginCard(), const SizedBox(height: AppSizes.paddingL), _buildFooter(), ], ), ), ), ), ), ); } Widget _buildHeader() { return Column( children: [ Container( width: 120, height: 120, decoration: BoxDecoration( color: Colors.white, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 20, offset: const Offset(0, 10), ), ], ), child: const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.terrain, size: 50, color: AppColors.primary), SizedBox(height: 4), Text('ASE', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: AppColors.primary)), ], ), ), ), const SizedBox(height: AppSizes.paddingL), const Text('ASE Monitor', style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Colors.white)), const SizedBox(height: AppSizes.paddingS), Text('Advanced Slope Engineering', style: TextStyle(fontSize: 16, color: Colors.white.withOpacity(0.9))), ], ); } Widget _buildLoginCard() { return Card( elevation: 8, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(AppSizes.radiusL)), child: Padding( padding: const EdgeInsets.all(AppSizes.paddingL), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Text('Accedi', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), textAlign: TextAlign.center), const SizedBox(height: AppSizes.paddingL), TextFormField( controller: _emailController, keyboardType: TextInputType.emailAddress, decoration: const InputDecoration(labelText: 'Email', hintText: 'nome@azienda.it', prefixIcon: Icon(Icons.email_outlined)), validator: (value) { if (value == null || value.isEmpty) return 'Inserisci email'; if (!value.contains('@')) return 'Email non valida'; return null; }, ), const SizedBox(height: AppSizes.paddingM), TextFormField( controller: _passwordController, obscureText: _obscurePassword, decoration: InputDecoration( labelText: 'Password', prefixIcon: const Icon(Icons.lock_outline), suffixIcon: IconButton( icon: Icon(_obscurePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined), onPressed: () => setState(() => _obscurePassword = !_obscurePassword), ), ), validator: (value) => (value == null || value.isEmpty) ? 'Inserisci password' : null, onFieldSubmitted: (_) => _handleLogin(), ), if (_errorMessage != null) ...[ const SizedBox(height: AppSizes.paddingM), Container( padding: const EdgeInsets.all(AppSizes.paddingM), decoration: BoxDecoration( color: AppColors.critical.withOpacity(0.1), borderRadius: BorderRadius.circular(AppSizes.radiusS), ), child: Row( children: [ const Icon(Icons.error_outline, color: AppColors.critical, size: 20), const SizedBox(width: AppSizes.paddingS), Expanded(child: Text(_errorMessage!, style: const TextStyle(color: AppColors.critical))), ], ), ), ], const SizedBox(height: AppSizes.paddingL), SizedBox( height: AppSizes.buttonHeight, child: ElevatedButton( onPressed: _isLoading ? null : _handleLogin, child: _isLoading ? const SizedBox(width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white))) : const Row(mainAxisAlignment: MainAxisAlignment.center, children: [Icon(Icons.login), SizedBox(width: 8), Text('Accedi')]), ), ), ], ), ), ), ); } Widget _buildFooter() { return Column( children: [ Text('Versione ${AppConstants.appVersion}', style: TextStyle(color: Colors.white.withOpacity(0.7), fontSize: 12)), const SizedBox(height: 4), Text('© 2025 ${AppConstants.companyName}', style: TextStyle(color: Colors.white.withOpacity(0.7), fontSize: 12), textAlign: TextAlign.center), ], ); } } DART_EOF echo -e "${GREEN}✓${NC} login_screen.dart creato" # --- Home Screen --- echo "Creando lib/screens/home_screen.dart..." cat > lib/screens/home_screen.dart << 'DART_EOF' import 'package:flutter/material.dart'; import '../utils/constants.dart'; import '../models/allarme.dart'; import '../services/api_service.dart'; import '../widgets/allarme_card.dart'; import 'profile_screen.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State { final _apiService = ApiService(); List _allarmi = []; bool _isLoading = true; String? _errorMessage; String? _selectedSeverita; @override void initState() { super.initState(); _loadAllarmi(); } Future _loadAllarmi({bool refresh = false}) async { if (refresh) setState(() => _isLoading = true); try { final response = await _apiService.getAllarmi(severita: _selectedSeverita); setState(() { _allarmi = response.items; _isLoading = false; }); } catch (e) { setState(() { _errorMessage = 'Errore caricamento allarmi'; _isLoading = false; }); } } void _showFilterDialog() { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Filtri'), content: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( title: const Text('Tutti'), leading: Radio(value: null, groupValue: _selectedSeverita, onChanged: (val) { Navigator.pop(context); setState(() => _selectedSeverita = val); _loadAllarmi(refresh: true); }), ), ListTile( title: const Text('CRITICO'), leading: Radio(value: 'critical', groupValue: _selectedSeverita, onChanged: (val) { Navigator.pop(context); setState(() => _selectedSeverita = val); _loadAllarmi(refresh: true); }), ), ListTile( title: const Text('AVVISO'), leading: Radio(value: 'warning', groupValue: _selectedSeverita, onChanged: (val) { Navigator.pop(context); setState(() => _selectedSeverita = val); _loadAllarmi(refresh: true); }), ), ], ), ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Column( children: [Text('ASE Monitor'), Text('Allarmi Attivi', style: TextStyle(fontSize: 12))], ), actions: [ IconButton(icon: const Icon(Icons.filter_list), onPressed: _showFilterDialog), IconButton(icon: const Icon(Icons.person), onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const ProfileScreen()))), ], ), body: _isLoading ? const Center(child: CircularProgressIndicator()) : _errorMessage != null ? Center(child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [const Icon(Icons.error_outline, size: 64, color: AppColors.critical), Text(_errorMessage!)])) : _allarmi.isEmpty ? const Center(child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [Icon(Icons.check_circle_outline, size: 64, color: AppColors.success), SizedBox(height: 16), Text('Nessun allarme', style: AppTextStyles.h3)])) : RefreshIndicator( onRefresh: () => _loadAllarmi(refresh: true), child: ListView.builder( padding: const EdgeInsets.all(AppSizes.paddingM), itemCount: _allarmi.length, itemBuilder: (context, index) => AllarmeCard(allarme: _allarmi[index]), ), ), floatingActionButton: FloatingActionButton( onPressed: () => _loadAllarmi(refresh: true), child: const Icon(Icons.refresh), ), ); } } DART_EOF echo -e "${GREEN}✓${NC} home_screen.dart creato" # --- Profile Screen --- echo "Creando lib/screens/profile_screen.dart..." cat > lib/screens/profile_screen.dart << 'DART_EOF' import 'package:flutter/material.dart'; import '../utils/constants.dart'; import '../services/auth_service.dart'; import 'login_screen.dart'; class ProfileScreen extends StatelessWidget { const ProfileScreen({super.key}); Future _handleLogout(BuildContext context) async { final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Conferma Logout'), content: const Text('Sei sicuro di voler uscire?'), actions: [ TextButton(onPressed: () => Navigator.pop(context, false), child: const Text('Annulla')), ElevatedButton( onPressed: () => Navigator.pop(context, true), style: ElevatedButton.styleFrom(backgroundColor: AppColors.critical), child: const Text('Esci'), ), ], ), ); if (confirmed == true && context.mounted) { await AuthService().logout(); if (context.mounted) { Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (_) => const LoginScreen()), (_) => false); } } } @override Widget build(BuildContext context) { final user = AuthService().currentUser; return Scaffold( appBar: AppBar(title: const Text('Profilo')), body: user == null ? const Center(child: CircularProgressIndicator()) : SingleChildScrollView( padding: const EdgeInsets.all(AppSizes.paddingM), child: Column( children: [ Card( child: Padding( padding: const EdgeInsets.all(AppSizes.paddingL), child: Column( children: [ CircleAvatar( radius: 50, backgroundColor: AppColors.primary, child: Text('${user.nome[0]}${user.cognome[0]}'.toUpperCase(), style: const TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Colors.white)), ), const SizedBox(height: AppSizes.paddingM), Text(user.nomeCompleto, style: AppTextStyles.h2, textAlign: TextAlign.center), Text(user.email, style: AppTextStyles.bodyMedium.copyWith(color: AppColors.textSecondary)), const SizedBox(height: AppSizes.paddingM), Container( padding: const EdgeInsets.symmetric(horizontal: AppSizes.paddingM, vertical: AppSizes.paddingS), decoration: BoxDecoration( color: AppColors.primary.withOpacity(0.1), borderRadius: BorderRadius.circular(AppSizes.radiusL), ), child: Text(user.ruolo.toUpperCase(), style: const TextStyle(color: AppColors.primary, fontWeight: FontWeight.w600)), ), ], ), ), ), const SizedBox(height: AppSizes.paddingM), Card( child: Column( children: [ ListTile(leading: const Icon(Icons.business_outlined), title: const Text('ID Cliente'), trailing: Text(user.clienteId.toString())), ListTile(leading: const Icon(Icons.verified_outlined), title: const Text('Versione'), trailing: Text(AppConstants.appVersion)), ], ), ), const SizedBox(height: AppSizes.paddingM), SizedBox( width: double.infinity, height: AppSizes.buttonHeight, child: ElevatedButton.icon( onPressed: () => _handleLogout(context), icon: const Icon(Icons.logout), label: const Text('Esci'), style: ElevatedButton.styleFrom(backgroundColor: AppColors.critical), ), ), ], ), ), ); } } DART_EOF echo -e "${GREEN}✓${NC} profile_screen.dart creato" # =========================================== # WIDGETS # =========================================== # --- Allarme Card --- echo "Creando lib/widgets/allarme_card.dart..." cat > lib/widgets/allarme_card.dart << 'DART_EOF' import 'package:flutter/material.dart'; import '../models/allarme.dart'; import '../utils/constants.dart'; class AllarmeCard extends StatelessWidget { final Allarme allarme; const AllarmeCard({super.key, required this.allarme}); Color _getSeverityColor() { switch (allarme.severita) { case 'critical': return AppColors.critical; case 'warning': return AppColors.warning; case 'info': return AppColors.info; default: return AppColors.textSecondary; } } @override Widget build(BuildContext context) { return Card( margin: const EdgeInsets.only(bottom: AppSizes.paddingM), child: Padding( padding: const EdgeInsets.all(AppSizes.paddingM), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: _getSeverityColor(), borderRadius: BorderRadius.circular(8), ), child: Text(allarme.severitaReadable, style: const TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold)), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: AppColors.info.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Text(allarme.statoReadable, style: const TextStyle(fontSize: 12)), ), ], ), const SizedBox(height: 12), Text(allarme.titolo, style: AppTextStyles.h3, maxLines: 2), const SizedBox(height: 8), Row( children: [ Icon(AlarmTypeIcons.getIcon(allarme.tipo), size: 16, color: AppColors.textSecondary), const SizedBox(width: 8), Text(allarme.tipoReadable, style: AppTextStyles.bodyMedium.copyWith(color: AppColors.textSecondary)), ], ), if (allarme.descrizione != null) ...[ const SizedBox(height: 8), Text(allarme.descrizione!, style: AppTextStyles.bodyMedium, maxLines: 2, overflow: TextOverflow.ellipsis), ], if (allarme.valoreRilevato != null && allarme.valoreSoglia != null) ...[ const SizedBox(height: 12), Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: _getSeverityColor().withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Icon(Icons.show_chart, size: 16, color: _getSeverityColor()), const SizedBox(width: 8), Expanded( child: Text( 'Valore: ${allarme.valoreRilevato!.toStringAsFixed(2)} ${allarme.unitaMisura ?? ''} (soglia: ${allarme.valoreSoglia!.toStringAsFixed(2)})', style: AppTextStyles.bodySmall, ), ), ], ), ), ], ], ), ), ); } } DART_EOF echo -e "${GREEN}✓${NC} allarme_card.dart creato" # =========================================== # RIEPILOGO # =========================================== echo "" echo "========================================" echo -e "${GREEN}✓ Tutti i file creati con successo!${NC}" echo "========================================" echo "" # Conta file totali TOTAL=$(find lib -name "*.dart" | wc -l) echo "File Dart totali: $TOTAL" echo "" echo "File creati:" echo " ✓ lib/screens/login_screen.dart" echo " ✓ lib/screens/home_screen.dart" echo " ✓ lib/screens/profile_screen.dart" echo " ✓ lib/widgets/allarme_card.dart" echo "" echo "File già esistenti (creati precedentemente):" echo " ✓ lib/main.dart" echo " ✓ lib/utils/constants.dart" echo " ✓ lib/utils/theme.dart" echo " ✓ lib/models/user.dart" echo " ✓ lib/models/allarme.dart" echo " ✓ lib/services/api_service.dart" echo " ✓ lib/services/auth_service.dart" echo " ✓ lib/services/notification_service.dart" echo "" echo "========================================" echo "Prossimi passi:" echo "========================================" echo "1. Configura Firebase: flutterfire configure" echo "2. Testa l'app: flutter run" echo "3. Build APK: flutter build apk --debug" echo "" echo "Vedi README.md per istruzioni complete" echo ""