import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../services/google_calendar_service.dart'; import '../utils/translations.dart'; class CalendarScreen extends StatefulWidget { const CalendarScreen({super.key}); @override State createState() => _CalendarScreenState(); } class _CalendarScreenState extends State { final GoogleCalendarService _calendarService = GoogleCalendarService(); bool _isLoading = false; List _events = []; String? _errorMessage; String _bulletColor = 'Blue'; String _selectedLanguage = 'English'; @override void initState() { super.initState(); initializeDateFormatting('sl_SI', null); // Initialize Slovenian locale _loadBulletColor(); _loadLanguage(); _loadEvents(); } String _t(String key) { return AppTranslations.translate(key, _selectedLanguage); } Future _loadLanguage() async { final prefs = await SharedPreferences.getInstance(); setState(() { _selectedLanguage = prefs.getString('language') ?? 'English'; }); } @override void dispose() { _calendarService.dispose(); super.dispose(); } Future _loadBulletColor() async { final prefs = await SharedPreferences.getInstance(); setState(() { _bulletColor = prefs.getString('bulletColor') ?? 'Blue'; }); } Color _getAccentColor() { switch (_bulletColor.toLowerCase()) { case 'blue': return Colors.blue; case 'green': return Colors.green; case 'yellow': return Colors.yellow; case 'purple': return Colors.purple; case 'cyan': return Colors.cyan; case 'teal': return Colors.teal; case 'indigo': return Colors.indigo; case 'pink': return Colors.pink; case 'lime': return Colors.lime; case 'mint': return Colors.greenAccent; case 'orange': return Colors.orange; case 'violet': return Colors.deepPurpleAccent; case 'amber': return Colors.amber; case 'brown': return Colors.brown; case 'grey': return Colors.grey; default: return Colors.blue; } } bool _isValidEvent(CalendarEvent event) { // Filter out events that are just single names or too short if (event.summary == null || event.summary!.trim().isEmpty) { return false; } final summary = event.summary!.trim(); // Exclude if it's just a single word (likely just a name) if (!summary.contains(' ') && summary.length < 15) { return false; } return true; } Future _loadEvents() async { setState(() { _isLoading = true; _errorMessage = null; }); try { final events = await _calendarService.getEvents( startDate: DateTime.now(), endDate: DateTime.now().add(const Duration(days: 365)), ); // Get today's date at midnight for comparison final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); // Filter: valid events AND events from today onwards final filteredEvents = events.where((event) { if (!_isValidEvent(event)) return false; // Only include events that are today or in the future if (event.startTime != null) { final eventDate = DateTime( event.startTime!.year, event.startTime!.month, event.startTime!.day, ); return eventDate.isAtSameMomentAs(today) || eventDate.isAfter(today); } return false; }).toList(); filteredEvents.sort((a, b) { if (a.startTime == null) return 1; if (b.startTime == null) return -1; return a.startTime!.compareTo(b.startTime!); }); if (mounted) { setState(() { _events = filteredEvents; }); } } catch (e) { if (mounted) { setState(() { _errorMessage = 'Error loading events: $e'; }); } } finally { if (mounted) { setState(() => _isLoading = false); } } } @override Widget build(BuildContext context) { final accentColor = _getAccentColor(); if (_isLoading && _events.isEmpty) { return Scaffold( body: Center( child: CircularProgressIndicator(color: accentColor), ), ); } if (_errorMessage != null && _events.isEmpty) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error_outline, size: 64, color: accentColor), const SizedBox(height: 24), Padding( padding: const EdgeInsets.symmetric(horizontal: 32), child: Text( _errorMessage!, textAlign: TextAlign.center, style: const TextStyle(fontSize: 16), ), ), const SizedBox(height: 16), ElevatedButton.icon( onPressed: _loadEvents, icon: const Icon(Icons.refresh), label: Text(_t('Retry')), style: ElevatedButton.styleFrom( backgroundColor: accentColor, foregroundColor: Colors.white, ), ), ], ), ), ); } return Scaffold( backgroundColor: Colors.white, body: RefreshIndicator( onRefresh: _loadEvents, color: accentColor, child: _events.isEmpty ? ListView( children: [ SizedBox(height: MediaQuery.of(context).size.height * 0.3), Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.event_available, size: 64, color: Colors.grey[400]), const SizedBox(height: 16), Text( _t('No upcoming events'), style: const TextStyle(fontSize: 16, color: Colors.grey), ), const SizedBox(height: 8), Text( _t('Pull down to refresh'), style: TextStyle(fontSize: 14, color: Colors.grey[400]), ), ], ), ), ], ) : ListView.builder( itemCount: _events.length, padding: const EdgeInsets.all(8), itemBuilder: (context, index) { final event = _events[index]; return _buildEventCard(event, accentColor); }, ), ), ); } Widget _buildEventCard(CalendarEvent event, Color accentColor) { final startTime = event.startTime; final isAllDay = event.isAllDay; // Format the date for display String dateText = ''; bool isToday = false; if (startTime != null) { final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); final eventDay = DateTime(startTime.year, startTime.month, startTime.day); final difference = eventDay.difference(today).inDays; if (difference == 0) { dateText = _t('Today'); isToday = true; } else if (difference == 1) { dateText = _t('Tomorrow'); } else { // Use locale based on selected language final locale = _selectedLanguage == 'Slovenian' ? 'sl_SI' : 'en_US'; dateText = DateFormat('EEEE, MMMM d', locale).format(startTime); // Capitalize first letter of day and month if (dateText.isNotEmpty) { dateText = dateText[0].toUpperCase() + dateText.substring(1); } } } return Container( margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Date header Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: isToday ? accentColor.withValues(alpha: 0.15) : Colors.grey[200], borderRadius: const BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( children: [ // Small colored indicator Container( width: 4, height: 16, decoration: BoxDecoration( color: accentColor, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 8), Text( dateText, style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: isToday ? accentColor : Colors.black87, ), ), if (isToday) ...[ const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: accentColor, borderRadius: BorderRadius.circular(8), ), child: Text( _t('NOW'), style: const TextStyle( fontSize: 10, fontWeight: FontWeight.bold, color: Colors.white, ), ), ), ], ], ), ), // Main content Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Title with icon Row( children: [ Icon(Icons.event, size: 20, color: accentColor), const SizedBox(width: 8), Expanded( child: Text( event.summary ?? _t('Untitled Event'), style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: Colors.black87, ), ), ), ], ), const SizedBox(height: 8), // Time range (if not all day) if (!isAllDay && startTime != null && event.endTime != null) ...[ _buildDetailRowInline( icon: Icons.access_time, value: '${DateFormat('HH:mm').format(startTime.toLocal())} - ${DateFormat('HH:mm').format(event.endTime!.toLocal())}', accentColor: accentColor, ), const SizedBox(height: 6), ], if (isAllDay) ...[ _buildDetailRowInline( icon: Icons.access_time, value: _t('All day'), accentColor: accentColor, ), const SizedBox(height: 6), ], // Location if (event.location != null && event.location!.isNotEmpty) ...[ _buildDetailRowInline( icon: Icons.location_on, value: event.location!, accentColor: accentColor, ), ], ], ), ), ], ), ); } Widget _buildDetailRowInline({ required IconData icon, required String value, required Color accentColor, }) { return Row( children: [ Icon(icon, size: 16, color: accentColor), const SizedBox(width: 6), Expanded( child: Text( value, style: const TextStyle( fontSize: 13, color: Colors.black87, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ); } }