import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:it0_app/l10n/app_localizations.dart'; import '../../../../core/theme/app_colors.dart'; import '../../../../core/widgets/robot_avatar.dart'; import '../../../chat/presentation/providers/chat_providers.dart'; import '../../../notifications/presentation/providers/notification_providers.dart'; import '../../../settings/presentation/providers/settings_providers.dart'; // --------------------------------------------------------------------------- // Home page — "主页" Tab // Shows active agent cards + recent activity + welcome guide // --------------------------------------------------------------------------- class HomePage extends ConsumerWidget { const HomePage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final profile = ref.watch(accountProfileProvider); final chatState = ref.watch(chatProvider); final unreadCount = ref.watch(unreadNotificationCountProvider); final l10n = AppLocalizations.of(context); final greeting = _greeting(l10n); final name = profile.displayName.isNotEmpty ? profile.displayName : l10n.defaultUserName; return Scaffold( backgroundColor: AppColors.background, body: CustomScrollView( slivers: [ // ── AppBar ────────────────────────────────────────────────────── SliverAppBar( expandedHeight: 120, pinned: true, backgroundColor: AppColors.background, flexibleSpace: FlexibleSpaceBar( titlePadding: const EdgeInsets.fromLTRB(20, 0, 20, 16), title: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( l10n.homeGreeting(greeting, name), style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: AppColors.textPrimary, ), ), const SizedBox(height: 2), Text( AppLocalizations.of(context).homeSubtitle, style: const TextStyle( fontSize: 12, color: AppColors.textSecondary, ), ), ], ), ), actions: [ if (unreadCount > 0) Stack( children: [ const Padding( padding: EdgeInsets.all(12), child: Icon(Icons.notifications_outlined, color: AppColors.textSecondary), ), Positioned( right: 8, top: 8, child: Container( width: 16, height: 16, decoration: const BoxDecoration( color: AppColors.error, shape: BoxShape.circle, ), child: Center( child: Text( '$unreadCount', style: const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold, ), ), ), ), ), ], ), const SizedBox(width: 8), ], ), SliverPadding( padding: const EdgeInsets.fromLTRB(16, 0, 16, 100), sliver: SliverList( delegate: SliverChildListDelegate([ // ── Active agent status card ───────────────────────────── _AgentStatusCard(chatState: chatState), const SizedBox(height: 20), // ── IT0 official agent cards ───────────────────────────── _SectionHeader(title: AppLocalizations.of(context).officialAgentsSection), const SizedBox(height: 12), const _OfficialAgentsRow(), const SizedBox(height: 20), // ── Guide if nothing created yet ───────────────────────── _SectionHeader(title: AppLocalizations.of(context).myAgentsSection), const SizedBox(height: 12), const _MyAgentsPlaceholder(), const SizedBox(height: 20), // ── Quick tips ─────────────────────────────────────────── const _QuickTipsCard(), ]), ), ), ], ), ); } String _greeting(AppLocalizations l10n) { final hour = DateTime.now().hour; if (hour < 6) return l10n.greetingLateNight; if (hour < 12) return l10n.greetingEarlyMorning; if (hour < 14) return l10n.greetingNoon; if (hour < 18) return l10n.greetingAfternoon; return l10n.greetingEvening; } } // --------------------------------------------------------------------------- // Agent status card — shows current iAgent state // --------------------------------------------------------------------------- class _AgentStatusCard extends StatelessWidget { final ChatState chatState; const _AgentStatusCard({required this.chatState}); @override Widget build(BuildContext context) { final isActive = chatState.isStreaming; final l10n = AppLocalizations.of(context); final statusText = switch (chatState.agentStatus) { AgentStatus.idle => l10n.agentStatusIdle, AgentStatus.thinking => l10n.agentStatusThinking, AgentStatus.executing => l10n.agentStatusExecuting, AgentStatus.awaitingApproval => l10n.agentStatusAwaitingApproval, AgentStatus.error => l10n.agentStatusError, }; final robotState = switch (chatState.agentStatus) { AgentStatus.idle => RobotState.idle, AgentStatus.thinking => RobotState.thinking, AgentStatus.executing => RobotState.executing, AgentStatus.awaitingApproval => RobotState.alert, AgentStatus.error => RobotState.alert, }; return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(20), border: Border.all( color: isActive ? AppColors.primary.withOpacity(0.5) : AppColors.surfaceLight.withOpacity(0.3), ), gradient: isActive ? LinearGradient( colors: [ AppColors.primary.withOpacity(0.08), AppColors.surface, ], begin: Alignment.topLeft, end: Alignment.bottomRight, ) : null, ), child: Row( children: [ RobotAvatar(state: robotState, size: 64), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( AppLocalizations.of(context).appTitle, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: AppColors.textPrimary, ), ), const SizedBox(height: 4), Row( children: [ Container( width: 8, height: 8, decoration: BoxDecoration( shape: BoxShape.circle, color: isActive ? AppColors.success : AppColors.textMuted, ), ), const SizedBox(width: 6), Text( statusText, style: TextStyle( fontSize: 13, color: isActive ? AppColors.success : AppColors.textSecondary, ), ), ], ), if (chatState.messages.isNotEmpty) ...[ const SizedBox(height: 6), Text( l10n.agentInConversation(chatState.messages.length), style: const TextStyle( fontSize: 12, color: AppColors.textMuted, ), ), ], ], ), ), // Arrow hint const Icon(Icons.chevron_right, color: AppColors.textMuted), ], ), ); } } // --------------------------------------------------------------------------- // Section header // --------------------------------------------------------------------------- class _SectionHeader extends StatelessWidget { final String title; const _SectionHeader({required this.title}); @override Widget build(BuildContext context) { return Text( title, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), ); } } // --------------------------------------------------------------------------- // Official agents row (horizontal scroll cards) // --------------------------------------------------------------------------- class _OfficialAgentsRow extends StatelessWidget { const _OfficialAgentsRow(); @override Widget build(BuildContext context) { final l = AppLocalizations.of(context); final agents = [ _AgentCard( name: l.officialAgent4Name, desc: l.officialAgent4Desc, icon: Icons.work_outline, color: const Color(0xFFF59E0B), isOfficial: true, ), _AgentCard( name: l.officialAgent5Name, desc: l.officialAgent5Desc, icon: Icons.support_agent_outlined, color: const Color(0xFF10B981), isOfficial: true, ), _AgentCard( name: l.officialAgent6Name, desc: l.officialAgent6Desc, icon: Icons.campaign_outlined, color: const Color(0xFFEC4899), isOfficial: true, ), _AgentCard( name: l.officialAgent7Name, desc: l.officialAgent7Desc, icon: Icons.translate_outlined, color: const Color(0xFF8B5CF6), isOfficial: true, ), _AgentCard( name: l.officialAgent1Name, desc: l.officialAgent1Desc, icon: Icons.dns_outlined, color: const Color(0xFF6366F1), isOfficial: true, ), _AgentCard( name: l.officialAgent2Name, desc: l.officialAgent2Desc, icon: Icons.security_outlined, color: const Color(0xFF22C55E), isOfficial: true, ), _AgentCard( name: l.officialAgent3Name, desc: l.officialAgent3Desc, icon: Icons.storage_outlined, color: const Color(0xFF0EA5E9), isOfficial: true, ), ]; return SizedBox( height: 130, child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: agents.length, separatorBuilder: (_, __) => const SizedBox(width: 12), itemBuilder: (context, index) => agents[index], ), ); } } class _AgentCard extends StatelessWidget { final String name; final String desc; final IconData icon; final Color color; final bool isOfficial; const _AgentCard({ required this.name, required this.desc, required this.icon, required this.color, this.isOfficial = false, }); @override Widget build(BuildContext context) { return Container( width: 180, padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), border: Border.all(color: color.withOpacity(0.3)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 36, height: 36, decoration: BoxDecoration( color: color.withOpacity(0.15), borderRadius: BorderRadius.circular(10), ), child: Icon(icon, color: color, size: 20), ), if (isOfficial) ...[ const Spacer(), Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: color.withOpacity(0.15), borderRadius: BorderRadius.circular(6), ), child: Text( AppLocalizations.of(context).officialBadge, style: TextStyle( fontSize: 10, color: color, fontWeight: FontWeight.w600, ), ), ), ], ], ), const SizedBox(height: 10), Text( name, style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( desc, style: const TextStyle( fontSize: 11, color: AppColors.textMuted, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ); } } // --------------------------------------------------------------------------- // My agents placeholder — guides user to talk to iAgent // --------------------------------------------------------------------------- class _MyAgentsPlaceholder extends StatelessWidget { const _MyAgentsPlaceholder(); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), border: Border.all( color: AppColors.primary.withOpacity(0.2), style: BorderStyle.solid, ), ), child: Column( children: [ const Icon( Icons.add_circle_outline, size: 40, color: AppColors.primary, ), const SizedBox(height: 12), Text( AppLocalizations.of(context).noOwnAgentsTitle, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), ), const SizedBox(height: 6), Text( AppLocalizations.of(context).noOwnAgentsDesc, style: const TextStyle( fontSize: 13, color: AppColors.textSecondary, height: 1.5, ), textAlign: TextAlign.center, ), ], ), ); } } // --------------------------------------------------------------------------- // Quick tips card // --------------------------------------------------------------------------- class _QuickTipsCard extends StatelessWidget { const _QuickTipsCard(); @override Widget build(BuildContext context) { final l = AppLocalizations.of(context); final tips = [ l.quickTip1, l.quickTip2, l.quickTip3, l.quickTip4, ]; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( l.quickTipsHeader, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: AppColors.textSecondary, ), ), const SizedBox(height: 10), ...tips.map( (tip) => Padding( padding: const EdgeInsets.only(bottom: 8), child: Text( tip, style: const TextStyle( fontSize: 13, color: AppColors.textMuted, height: 1.4, ), ), ), ), ], ), ); } }