import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:shared_preferences/shared_preferences.dart'; class AppColors { static const primary = Color(0xFF8D0012); static const primaryDark = Color(0xFFB51621); static const darkBlue = Color(0xFF0B1C30); static const lightBg = Color(0xFFF8F9FF); static const border = Color(0xFFE4BEBA); static const textSecondary = Color(0xFF5B403E); static const green = Color(0xFF006760); static const greenDark = Color(0xFF00504A); static const blueAccent = Color(0xFFD3E4FE); static const blueLight = Color(0xFFEFF4FF); static const muted = Color(0xFF8F6F6D); static const surface = Color(0xFFFFFFFF); static const scaffold = Color(0xFFF1F5F9); } ThemeData buildAppTheme() { return ThemeData( useMaterial3: true, colorScheme: ColorScheme.fromSeed( seedColor: AppColors.primary, primary: AppColors.primary, surface: AppColors.lightBg, ), scaffoldBackgroundColor: AppColors.scaffold, textTheme: GoogleFonts.interTextTheme().copyWith( displayLarge: GoogleFonts.inter( fontWeight: FontWeight.w800, color: AppColors.darkBlue, ), bodyMedium: GoogleFonts.inter(color: AppColors.darkBlue), ), appBarTheme: AppBarTheme( backgroundColor: AppColors.surface, surfaceTintColor: Colors.transparent, elevation: 0, shadowColor: Colors.black12, scrolledUnderElevation: 1, titleTextStyle: GoogleFonts.inter( fontWeight: FontWeight.w800, fontSize: 16, color: AppColors.darkBlue, ), ), navigationBarTheme: NavigationBarThemeData( backgroundColor: AppColors.surface, indicatorColor: AppColors.primaryDark, labelTextStyle: WidgetStateProperty.resolveWith((states) { final active = states.contains(WidgetState.selected); return GoogleFonts.inter( fontSize: 10, fontWeight: FontWeight.w700, color: active ? AppColors.primary : AppColors.muted, ); }), iconTheme: WidgetStateProperty.resolveWith((states) { final active = states.contains(WidgetState.selected); return IconThemeData( color: active ? Colors.white : AppColors.muted, size: 18, ); }), ), inputDecorationTheme: InputDecorationTheme( border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.border), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.border), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.primary, width: 1.5), ), filled: true, fillColor: AppColors.surface, contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), ), ); } class WideTopBanner extends StatefulWidget { final bool backendReady; final String subtitle; final VoidCallback? onLogout; const WideTopBanner({ super.key, required this.backendReady, required this.subtitle, this.onLogout, }); @override State createState() => _WideTopBannerState(); } class _WideTopBannerState extends State { String _title = 'Doormile CRM'; @override void initState() { super.initState(); _loadUser(); } Future _loadUser() async { try { final prefs = await SharedPreferences.getInstance(); final name = prefs.getString('user_name'); if (name != null && name.isNotEmpty && mounted) { setState(() => _title = 'Hi, $name 👋'); } } catch (_) {} } @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Colors.white, Color(0xFFF8FAFC), ], ), border: Border.all(color: Colors.white, width: 2), boxShadow: [ BoxShadow( color: AppColors.darkBlue.withValues(alpha: 0.03), blurRadius: 10, offset: const Offset(0, 4), ), BoxShadow( color: AppColors.primary.withValues(alpha: 0.04), blurRadius: 20, offset: const Offset(0, 10), spreadRadius: -5, ), ], ), child: Row( children: [ Container( padding: const EdgeInsets.all(2.5), decoration: BoxDecoration( shape: BoxShape.circle, gradient: LinearGradient( colors: [ AppColors.primary.withValues(alpha: 0.8), AppColors.border, ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), boxShadow: [ BoxShadow( color: AppColors.primary.withValues(alpha: 0.2), blurRadius: 8, offset: const Offset(0, 3), ), ], ), child: Container( padding: const EdgeInsets.all(2), decoration: const BoxDecoration( color: Colors.white, shape: BoxShape.circle, ), child: ClipOval( child: Image.asset( 'assets/doormilelogoround.png', width: 38, height: 38, fit: BoxFit.cover, ), ), ), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( _title, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w800, color: AppColors.darkBlue, letterSpacing: -0.3, ), ), const SizedBox(height: 3), Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: AppColors.primary.withValues(alpha: 0.08), borderRadius: BorderRadius.circular(6), ), child: Text( widget.subtitle.toUpperCase(), style: const TextStyle( fontSize: 9, fontWeight: FontWeight.w800, letterSpacing: 1.2, color: AppColors.primary, ), ), ), ], ), ), _SyncStatusBadge(ready: widget.backendReady), if (widget.onLogout != null) ...[ const SizedBox(width: 8), GestureDetector( onTap: widget.onLogout, child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.white, shape: BoxShape.circle, border: Border.all(color: AppColors.border.withValues(alpha: 0.2)), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.02), blurRadius: 4, offset: const Offset(0, 2), ) ], ), child: const Icon( Icons.logout_rounded, size: 20, color: AppColors.primary, ), ), ), ], ], ), ); } } class _SyncStatusBadge extends StatelessWidget { final bool ready; const _SyncStatusBadge({required this.ready}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), decoration: BoxDecoration( color: ready ? const Color(0xFFECFDF5) : const Color(0xFFFFFBEB), borderRadius: BorderRadius.circular(20), border: Border.all( color: ready ? const Color(0xA110B981) : const Color(0xA1FBBF24), width: 1, ), boxShadow: [ BoxShadow( color: ready ? const Color(0xFF10B981).withValues(alpha: 0.1) : const Color(0xFFF59E0B).withValues(alpha: 0.1), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Row( mainAxisSize: MainAxisSize.min, children: [ _PulsingDot( color: ready ? const Color(0xFF10B981) : const Color(0xFFF59E0B), ), const SizedBox(width: 6), Text( ready ? 'SYNCED' : 'CACHED', style: TextStyle( fontSize: 9, fontWeight: FontWeight.w800, color: ready ? const Color(0xFF065F46) : const Color(0xFF92400E), letterSpacing: 0.5, ), ), ], ), ); } } class _PulsingDot extends StatefulWidget { final Color color; const _PulsingDot({required this.color}); @override State<_PulsingDot> createState() => _PulsingDotState(); } class _PulsingDotState extends State<_PulsingDot> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _animation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 1500), )..repeat(reverse: true); _animation = CurvedAnimation( parent: _controller, curve: Curves.easeInOut, ); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animation, builder: (context, child) { return Container( width: 6, height: 6, decoration: BoxDecoration( shape: BoxShape.circle, color: widget.color, boxShadow: [ BoxShadow( color: widget.color.withValues(alpha: 0.6 * _animation.value), blurRadius: 6 * _animation.value, spreadRadius: 2 * _animation.value, ), ], ), ); }, ); } }