import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:url_launcher/url_launcher.dart'; import '../services/google_calendar_service.dart'; import '../utils/translations.dart'; class Contact { final String firstName; final String lastName; final String phone; final String email; Contact({ required this.firstName, required this.lastName, required this.phone, required this.email, }); String get fullName => '$firstName $lastName'; } enum EventType { regular, // Dežurstvo and other regular events competition, // Liga and Tekmovanje (League and Competition events) sestanek, // Meetings } 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'; bool _showContacts = false; bool _showDebugPreview = false; // Members contact list final List _contacts = [ Contact(firstName: 'Domen', lastName: 'Pleterski', phone: '051227764', email: 'info@bl3k.com'), Contact(firstName: 'Ivan', lastName: 'Tandler', phone: '040471258', email: 'ivan.tandler@gmail.com'), Contact(firstName: 'Jože', lastName: 'Verhnjak', phone: '041443463', email: 'joze.verhnjak@gmail.com'), Contact(firstName: 'Branko', lastName: 'Pokeržnik', phone: '041291001', email: 'pokerznik.helena@gmail.com'), Contact(firstName: 'Franc', lastName: 'Žigart', phone: '041439597', email: 'franc.zigart@gmail.com'), Contact(firstName: 'Janez', lastName: 'Božič', phone: '041551704', email: 'janbozic@gmail.com'), Contact(firstName: 'Mitja', lastName: 'Čeh', phone: '070550383', email: 'mekabara@gmail.com'), Contact(firstName: 'Rado', lastName: 'Keter', phone: '041324529', email: 'rado.keter@gmail.com'), Contact(firstName: 'Karli', lastName: 'Proje', phone: '040424951', email: 'karl.project@gmail.com'), Contact(firstName: 'Jan', lastName: 'Pleterski', phone: '041842063', email: 'jan.pleterski1997@gmail.com'), Contact(firstName: 'Silvo', lastName: 'Poročnik', phone: '040860618', email: 'skobar.silvo@gmail.com'), Contact(firstName: 'Dušan', lastName: 'Onuk', phone: '070448303', email: 'dusan.onuk@gmail.com'), Contact(firstName: 'Matjaž', lastName: 'Pleterski', phone: '041603859', email: 'matjaz.pleterski@gmail.com'), Contact(firstName: 'Marko', lastName: 'Blimen', phone: '031610857', email: 'blimenmarko@gmail.com'), Contact(firstName: 'Doris', lastName: 'Fesel', phone: '070373987', email: 'doris.fesel@gmail.com'), ]; @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'; }); } void _toggleDebugPreview() { setState(() { _showDebugPreview = !_showDebugPreview; }); } @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 with empty summaries if (event.summary == null || event.summary!.trim().isEmpty) { return false; } final summary = event.summary!.trim(); // Only exclude very short single-character entries // This allows short event names like "Meeting", "Training", etc. if (summary.length < 2) { return false; } return true; } EventType? _getEventType(CalendarEvent event) { if (event.summary == null) return null; final summary = event.summary!.toLowerCase(); if (summary.contains('liga') || summary.contains('tekmovanje') || summary.contains('tekma')) { return EventType.competition; } else if (summary.contains('sestanek')) { return EventType.sestanek; } else if (summary.contains('dežurstvo')) { return EventType.regular; } // No matching keywords - skip this event return null; } 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 matching keywords AND events from today onwards final filteredEvents = events.where((event) { if (!_isValidEvent(event)) return false; // Check if event has a matching keyword type if (_getEventType(event) == null) 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); } } } Future _makePhoneCall(String phoneNumber) async { final Uri launchUri = Uri( scheme: 'tel', path: phoneNumber, ); try { await launchUrl(launchUri); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('${_t('Could not launch phone call: ')}$e')), ); } } } String _capitalize(String text) { return text.split(' ').map((word) { if (word.isEmpty) return word; return word[0].toUpperCase() + word.substring(1).toLowerCase(); }).join(' '); } void _copyToClipboard(String text, String label) { Clipboard.setData(ClipboardData(text: text)); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('$label ${_t('copied to clipboard')}'), duration: const Duration(seconds: 2), ), ); } void _showContactPopup(BuildContext context, Contact contact) { final accentColor = _getAccentColor(); showModalBottomSheet( context: context, backgroundColor: Colors.grey[900], shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), builder: (context) { return RepaintBoundary( child: Container( padding: const EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header with contact name Padding( padding: const EdgeInsets.only(bottom: 20), child: Row( children: [ Container( width: 50, height: 50, decoration: BoxDecoration( color: accentColor, borderRadius: BorderRadius.circular(10), ), child: Center( child: Text( contact.firstName[0].toUpperCase(), style: const TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold, ), ), ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _capitalize(contact.fullName), style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), ], ), ), ], ), ), // Phone number row Container( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12), decoration: BoxDecoration( color: Colors.grey[850], borderRadius: BorderRadius.circular(10), ), child: Row( children: [ Icon(Icons.phone, color: accentColor, size: 20), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _t('Phone'), style: const TextStyle( color: Colors.grey, fontSize: 11, ), ), Text( contact.phone, style: const TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.w600, ), ), ], ), ), Container( decoration: BoxDecoration( color: accentColor, borderRadius: BorderRadius.circular(8), ), child: Material( color: Colors.transparent, child: InkWell( onTap: () { _copyToClipboard(contact.phone, 'Phone number'); Navigator.pop(context); }, borderRadius: BorderRadius.circular(8), child: const Padding( padding: EdgeInsets.all(8), child: Icon( Icons.content_copy, color: Colors.white, size: 18, ), ), ), ), ), ], ), ), const SizedBox(height: 12), // Email row Container( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12), decoration: BoxDecoration( color: Colors.grey[850], borderRadius: BorderRadius.circular(10), ), child: Row( children: [ Icon(Icons.email, color: accentColor, size: 20), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _t('Email'), style: const TextStyle( color: Colors.grey, fontSize: 11, ), ), Text( contact.email.toLowerCase(), style: const TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.w600, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ), Container( decoration: BoxDecoration( color: accentColor, borderRadius: BorderRadius.circular(8), ), child: Material( color: Colors.transparent, child: InkWell( onTap: () { _copyToClipboard(contact.email, 'Email address'); Navigator.pop(context); }, borderRadius: BorderRadius.circular(8), child: const Padding( padding: EdgeInsets.all(8), child: Icon( Icons.content_copy, color: Colors.white, size: 18, ), ), ), ), ), ], ), ), ], ), ), ); }, ); } Widget _buildContactsList() { final accentColor = _getAccentColor(); return ListView.builder( itemCount: _contacts.length, padding: const EdgeInsets.all(8), itemBuilder: (context, index) { final contact = _contacts[index]; return GestureDetector( onTap: () => _makePhoneCall(contact.phone), onLongPress: () => _showContactPopup(context, contact), child: Container( margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8), decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(10), boxShadow: [ BoxShadow( color: Colors.grey.withValues(alpha: 0.08), blurRadius: 2, offset: const Offset(0, 1), ), ], ), child: Stack( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Colored header bar (like event cards) Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6), decoration: BoxDecoration( color: accentColor.withValues(alpha: 0.2), borderRadius: const BorderRadius.only( topLeft: Radius.circular(10), topRight: Radius.circular(10), ), ), child: Row( children: [ // Small colored indicator Container( width: 3, height: 12, decoration: BoxDecoration( color: accentColor, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 7), // Contact name Expanded( child: Text( _capitalize(contact.fullName), style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: accentColor, ), ), ), ], ), ), // Main content with phone and email info Padding( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.phone, size: 14, color: accentColor), const SizedBox(width: 6), Text( contact.phone, style: const TextStyle( fontSize: 12, color: Colors.black87, fontWeight: FontWeight.w500, ), ), ], ), const SizedBox(height: 4), Row( children: [ Icon(Icons.email, size: 14, color: accentColor), const SizedBox(width: 6), Expanded( child: Text( contact.email.toLowerCase(), style: const TextStyle( fontSize: 12, color: Colors.black87, fontWeight: FontWeight.w500, ), overflow: TextOverflow.ellipsis, ), ), ], ), ], ), ), ], ), // Phone button on the right (wider now) Positioned( right: 0, top: 0, bottom: 0, child: Container( width: 80, decoration: BoxDecoration( color: accentColor, borderRadius: const BorderRadius.only( topRight: Radius.circular(10), bottomRight: Radius.circular(10), ), ), child: Material( color: Colors.transparent, child: InkWell( onTap: () => _makePhoneCall(contact.phone), borderRadius: const BorderRadius.only( topRight: Radius.circular(10), bottomRight: Radius.circular(10), ), child: Center( child: Icon( Icons.call, color: Colors.white, size: 22, ), ), ), ), ), ), ], ), ), ); }, ); } @override Widget build(BuildContext context) { final accentColor = _getAccentColor(); // Debug preview - disabled by default, set _showDebugPreview = true to enable if (_showDebugPreview) { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( backgroundColor: Colors.grey[900], centerTitle: true, title: const Text( 'Debug Preview', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold), ), leading: IconButton( icon: const Icon(Icons.close, color: Colors.white), onPressed: _toggleDebugPreview, ), ), body: ListView( padding: const EdgeInsets.all(16), children: [ const SizedBox(height: 8), // Regular event Text( 'Regular Event (Dežurstvo)', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.grey[600], ), ), const SizedBox(height: 8), _buildSampleEventCard(accentColor, EventType.regular), const SizedBox(height: 24), // Competition event Text( 'Competition Event (Tekmovanje)', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.grey[600], ), ), const SizedBox(height: 8), _buildSampleEventCard(accentColor, EventType.competition), const SizedBox(height: 24), // Meeting event Text( 'Meeting Event (Sestanek)', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.grey[600], ), ), const SizedBox(height: 8), _buildSampleEventCard(accentColor, EventType.sestanek), const SizedBox(height: 24), ], ), ); } 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: Column( children: [ // Tab buttons Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( color: Colors.grey[900], border: Border( bottom: BorderSide(color: Colors.grey[800]!, width: 1), ), ), child: Row( children: [ Expanded( child: GestureDetector( onTap: () { setState(() { _showContacts = false; }); }, child: Container( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8), decoration: BoxDecoration( color: !_showContacts ? Colors.white : Colors.transparent, borderRadius: BorderRadius.circular(10), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.calendar_month, color: !_showContacts ? Colors.black : Colors.grey[400], size: 18, ), const SizedBox(width: 6), Text( _t('Calendar'), style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: !_showContacts ? Colors.black : Colors.grey[400], ), ), ], ), ), ), ), const SizedBox(width: 6), Expanded( child: GestureDetector( onTap: () { setState(() { _showContacts = true; }); }, child: Container( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8), decoration: BoxDecoration( color: _showContacts ? Colors.white : Colors.transparent, borderRadius: BorderRadius.circular(10), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.people, color: _showContacts ? Colors.black : Colors.grey[400], size: 18, ), const SizedBox(width: 6), Text( _t('Contacts'), style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: _showContacts ? Colors.black : Colors.grey[400], ), ), ], ), ), ), ), if (_showDebugPreview) ...[ const SizedBox(width: 6), GestureDetector( onTap: _toggleDebugPreview, child: Container( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), decoration: BoxDecoration( color: Colors.grey[800], borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.amber, width: 1), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.bug_report, color: Colors.amber, size: 16, ), const SizedBox(width: 4), const Text( 'Debug', style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: Colors.amber, ), ), ], ), ), ), ], ], ), ), // Body content Expanded( child: _showContacts ? _buildContactsList() : RefreshIndicator( onRefresh: _loadEvents, color: accentColor, child: _events.isEmpty ? ListView( children: [ const SizedBox(height: 24), 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]), ), ], ), ), const SizedBox(height: 32), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( _t('Example Event Preview'), style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.grey[700], ), textAlign: TextAlign.center, ), ), const SizedBox(height: 16), _buildSampleEventCard(accentColor, EventType.regular), const SizedBox(height: 8), _buildSampleEventCard(accentColor, EventType.competition), const SizedBox(height: 8), _buildSampleEventCard(accentColor, EventType.sestanek), ], ) : ListView.builder( itemCount: _events.length, padding: const EdgeInsets.all(8), itemBuilder: (context, index) { final event = _events[index]; return _buildEventCard(event, accentColor); }, ), ), ), ], ), ); } Widget _buildSampleEventCard(Color accentColor, EventType eventType) { // Color coding for each event type Color cardAccentColor; Color backgroundColor = Colors.grey[300]!; IconData eventIcon; String title; String time; switch (eventType) { case EventType.competition: cardAccentColor = Colors.orange; eventIcon = Icons.emoji_events; title = _t('League / Competition'); time = '09:00 - 18:00'; break; case EventType.sestanek: cardAccentColor = Colors.red; eventIcon = Icons.groups; title = _t('Team Meeting'); time = '18:00 - 19:00'; break; case EventType.regular: cardAccentColor = Colors.blue; eventIcon = Icons.event; title = _t('Duty Shift'); time = '14:00 - 16:00'; break; } return RepaintBoundary( child: Container( margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular(12), boxShadow: [ // Card glow effect for today's events BoxShadow( color: cardAccentColor.withValues(alpha: 0.3), blurRadius: 16, offset: const Offset(0, 4), ), // Subtle inner shadow BoxShadow( color: Colors.grey.withValues(alpha: 0.1), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Date header Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: cardAccentColor.withValues(alpha: 0.2), 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: cardAccentColor, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 8), Text( _t('Today'), style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: cardAccentColor, ), ), const Spacer(), // Event type badge - filled badges for all types Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), decoration: BoxDecoration( color: cardAccentColor, borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(eventIcon, size: 12, color: Colors.white), const SizedBox(width: 4), Text( eventType == EventType.competition ? _t('TEKMOVANJE') : eventType == EventType.sestanek ? _t('SESTANEK') : _t('DEŽURSTVO'), style: const TextStyle( fontSize: 10, fontWeight: FontWeight.bold, color: Colors.white, letterSpacing: 0.8, ), ), ], ), ), ], ), ), // Main content Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Title with icon Row( children: [ Icon(eventIcon, size: 20, color: cardAccentColor), const SizedBox(width: 8), Expanded( child: Text( title, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: Colors.black87, ), ), ), ], ), const SizedBox(height: 8), // Time range _buildDetailRowInline( icon: Icons.access_time, value: time, accentColor: cardAccentColor, ), const SizedBox(height: 6), // Location _buildDetailRowInline( icon: Icons.location_on, value: _t('Strelišče Ljubljana'), accentColor: cardAccentColor, ), ], ), ), ], ), ), ); } Widget _buildEventCard(CalendarEvent event, Color accentColor) { final startTime = event.startTime; final isAllDay = event.isAllDay; final eventType = _getEventType(event); // Color coding for each event type Color cardAccentColor; Color backgroundColor; IconData eventIcon; switch (eventType) { case EventType.competition: cardAccentColor = Colors.orange; backgroundColor = Colors.grey[300]!; eventIcon = Icons.emoji_events; // Trophy icon for competitions break; case EventType.sestanek: cardAccentColor = Colors.red; backgroundColor = Colors.grey[300]!; eventIcon = Icons.groups; // People icon for meetings break; case EventType.regular: cardAccentColor = Colors.blue; backgroundColor = Colors.grey[300]!; eventIcon = Icons.event; break; case null: // This case should never happen due to filtering, but required by compiler return const SizedBox(); } // 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 RepaintBoundary( child: Container( margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular(12), boxShadow: [ // Card glow effect (stronger for today's events) if (isToday) BoxShadow( color: cardAccentColor.withValues(alpha: 0.3), blurRadius: 16, offset: const Offset(0, 4), ), // Subtle inner shadow BoxShadow( color: Colors.grey.withValues(alpha: 0.1), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Date header Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: isToday ? cardAccentColor.withValues(alpha: 0.2) : cardAccentColor.withValues(alpha: 0.1), 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: cardAccentColor, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 8), Text( dateText, style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: cardAccentColor, ), ), const Spacer(), // Event type badge - filled badges for all types Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), decoration: BoxDecoration( color: cardAccentColor, borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(eventIcon, size: 12, color: Colors.white), const SizedBox(width: 4), Text( eventType == EventType.competition ? _t('TEKMOVANJE') : eventType == EventType.sestanek ? _t('SESTANEK') : _t('DEŽURSTVO'), style: const TextStyle( fontSize: 10, fontWeight: FontWeight.bold, color: Colors.white, letterSpacing: 0.8, ), ), ], ), ), ], ), ), // Main content Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Title with icon Row( children: [ Icon(eventIcon, size: 20, color: cardAccentColor), const SizedBox(width: 8), Expanded( child: Text( event.summary ?? _t('Untitled Event'), style: 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: cardAccentColor, ), const SizedBox(height: 6), ], if (isAllDay) ...[ _buildDetailRowInline( icon: Icons.access_time, value: _t('All day'), accentColor: cardAccentColor, ), const SizedBox(height: 6), ], // Location if (event.location != null && event.location!.isNotEmpty) ...[ _buildDetailRowInline( icon: Icons.location_on, value: event.location!, accentColor: cardAccentColor, ), ], ], ), ), ], ), ), ); } 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, ), ), ], ); } }