diff --git a/analysis_errors.txt b/analysis_errors.txt new file mode 100644 index 0000000..9d803b6 Binary files /dev/null and b/analysis_errors.txt differ diff --git a/analysis_full.txt b/analysis_full.txt new file mode 100644 index 0000000..ecc6eaa Binary files /dev/null and b/analysis_full.txt differ diff --git a/analysis_report.txt b/analysis_report.txt new file mode 100644 index 0000000..ecc6eaa Binary files /dev/null and b/analysis_report.txt differ diff --git a/lib/features/auth/screens/forgot_password_screen.dart b/lib/features/auth/screens/forgot_password_screen.dart new file mode 100644 index 0000000..69d6514 --- /dev/null +++ b/lib/features/auth/screens/forgot_password_screen.dart @@ -0,0 +1,187 @@ +import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:socialbuddy_mobile/core/constants/colors.dart'; + +class ForgotPasswordScreen extends StatefulWidget { + const ForgotPasswordScreen({super.key}); + + @override + State createState() => _ForgotPasswordScreenState(); +} + +class _ForgotPasswordScreenState extends State { + final _emailController = TextEditingController(); + bool _isLoading = false; + + @override + void dispose() { + _emailController.dispose(); + super.dispose(); + } + + Future _handleResetPassword() async { + if (_emailController.text.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Please enter your email")), + ); + return; + } + + setState(() => _isLoading = true); + // Mimic API call logic as in web + await Future.delayed(const Duration(seconds: 2)); + + if (mounted) { + setState(() => _isLoading = false); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Password reset link sent to your email!")), + ); + Navigator.pop(context); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFF0A0B17), + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () => Navigator.pop(context), + ), + ), + body: Stack( + children: [ + // Background Glows + Positioned( + top: 100, + right: 50, + child: _buildGlowBlob(const Color(0xFF1D8BE0), 120), + ), + Positioned( + bottom: 100, + left: 50, + child: _buildGlowBlob(const Color(0xFFDB21D9), 120), + ), + + Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(24), + child: ClipRRect( + borderRadius: BorderRadius.circular(24), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: Container( + padding: const EdgeInsets.all(32), + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.05), + borderRadius: BorderRadius.circular(24), + border: Border.all(color: Colors.white.withOpacity(0.2)), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.lock_reset, size: 80, color: Colors.blueAccent), + const SizedBox(height: 24), + Text( + "Forgot Password?", + style: GoogleFonts.nunito( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 12), + Text( + "No worries! Enter your email and we'll send you a link to reset your password.", + textAlign: TextAlign.center, + style: GoogleFonts.nunito( + color: Colors.white.withOpacity(0.6), + fontSize: 14, + ), + ), + const SizedBox(height: 32), + + TextField( + controller: _emailController, + style: const TextStyle(color: Colors.white), + decoration: _inputDecoration("Email Address", Icons.email_outlined), + ), + + const SizedBox(height: 32), + + SizedBox( + width: double.infinity, + height: 50, + child: Container( + decoration: BoxDecoration( + gradient: const LinearGradient( + colors: [Color(0xFF2563EB), Color(0xFFEC4899)], + ), + borderRadius: BorderRadius.circular(12), + ), + child: ElevatedButton( + onPressed: _isLoading ? null : _handleResetPassword, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.transparent, + shadowColor: Colors.transparent, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + ), + child: _isLoading + ? const CircularProgressIndicator(color: Colors.white) + : const Text("SEND RESET LINK", style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white)), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ], + ), + ); + } + + Widget _buildGlowBlob(Color color, double size) { + return Container( + width: size, + height: size, + decoration: BoxDecoration( + color: color.withOpacity(0.3), + shape: BoxShape.circle, + boxShadow: [ + BoxShadow(color: color.withOpacity(0.5), blurRadius: 100, spreadRadius: 20), + ], + ), + ); + } + + InputDecoration _inputDecoration(String label, IconData icon) { + return InputDecoration( + hintText: label, + hintStyle: TextStyle(color: Colors.white.withOpacity(0.5)), + prefixIcon: Icon(icon, color: Colors.white70), + filled: true, + fillColor: const Color(0xFF070D1E).withOpacity(0.7), + contentPadding: const EdgeInsets.symmetric(vertical: 16), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.white.withOpacity(0.15)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.white.withOpacity(0.15)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.white.withOpacity(0.3)), + ), + ); + } +} diff --git a/lib/features/auth/screens/login_screen.dart b/lib/features/auth/screens/login_screen.dart index c5b8f48..9d8ea71 100644 --- a/lib/features/auth/screens/login_screen.dart +++ b/lib/features/auth/screens/login_screen.dart @@ -5,6 +5,7 @@ import 'package:provider/provider.dart'; import '../providers/auth_provider.dart'; import '../../home/screens/home_screen.dart'; import 'signup_screen.dart'; +import 'forgot_password_screen.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @@ -139,7 +140,12 @@ class _LoginScreenState extends State { Align( alignment: Alignment.centerRight, child: TextButton( - onPressed: () {}, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const ForgotPasswordScreen()), + ); + }, child: const Text("Forgot Password?", style: TextStyle(color: Colors.blueAccent)), ), ), diff --git a/lib/features/automation/screens/automation_screen.dart b/lib/features/automation/screens/automation_screen.dart index dd1ba74..5391be4 100644 --- a/lib/features/automation/screens/automation_screen.dart +++ b/lib/features/automation/screens/automation_screen.dart @@ -5,6 +5,9 @@ import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; import '../../../core/constants/colors.dart'; import '../../../core/constants/api_constants.dart'; +import '../../connect/screens/social_connect_details_screen.dart'; +import '../../connect/screens/channels_screen.dart'; +import '../../posts/screens/posts_screen.dart'; class AutomationScreen extends StatefulWidget { const AutomationScreen({super.key}); diff --git a/lib/features/connect/screens/channels_screen.dart b/lib/features/connect/screens/channels_screen.dart index 1ba16a9..a479b85 100644 --- a/lib/features/connect/screens/channels_screen.dart +++ b/lib/features/connect/screens/channels_screen.dart @@ -5,6 +5,8 @@ import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; import '../../../core/constants/colors.dart'; import '../../../core/constants/api_constants.dart'; +import '../../posts/screens/posts_screen.dart'; +import '../../automation/screens/automation_screen.dart'; class ChannelsScreen extends StatefulWidget { const ChannelsScreen({super.key}); @@ -37,16 +39,18 @@ class _ChannelsScreenState extends State { ); if (response.statusCode == 200) { - _channels = jsonDecode(response.body); + setState(() { + _channels = jsonDecode(response.body); + }); } if (_connectedChannelId != null) { await _loadAccountDetails(); } } catch (e) { - print("Error loading channels: $e"); + debugPrint("Error loading channels: $e"); } finally { - setState(() => _isLoading = false); + if (mounted) setState(() => _isLoading = false); } } @@ -58,10 +62,12 @@ class _ChannelsScreenState extends State { Uri.parse('${ApiConstants.liveBaseUrl}/social/account?userId=$email'), ); if (response.statusCode == 200) { - _accountData = jsonDecode(response.body); + setState(() { + _accountData = jsonDecode(response.body); + }); } } catch (e) { - print("Error loading account details: $e"); + debugPrint("Error loading account details: $e"); } } @@ -79,20 +85,31 @@ class _ChannelsScreenState extends State { if (response.statusCode == 200) { final data = jsonDecode(response.body); - final savedId = data['data']['channel_id'] ?? channelId; + final savedId = data['data']?['channel_id'] ?? channelId; await prefs.setString('connectedChannel', savedId); - setState(() => _connectedChannelId = savedId); + setState(() => _connectedChannelId = savedId.toString()); await _loadAccountDetails(); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("$channelName connected successfully!")), - ); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("$channelName connected successfully!")), + ); + } + } else { + final error = jsonDecode(response.body); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(error['error'] ?? "Failed to connect $channelName")), + ); + } } } catch (e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("Failed to connect $channelName")), - ); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Error: $e")), + ); + } } finally { - setState(() => _isLoading = false); + if (mounted) setState(() => _isLoading = false); } } @@ -103,17 +120,28 @@ class _ChannelsScreenState extends State { _connectedChannelId = null; _accountData = null; }); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text("Channel removed successfully!")), - ); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Channel removed successfully!")), + ); + } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.darkBg, + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + title: Text("Instagram Channels", style: GoogleFonts.nunito(color: Colors.white, fontWeight: FontWeight.bold)), + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () => Navigator.pop(context), + ), + ), body: _isLoading - ? const Center(child: CircularProgressIndicator(color: AppColors.primary)) + ? const Center(child: CircularProgressIndicator(color: Colors.pinkAccent)) : _connectedChannelId != null ? _buildAccountDetails() : _buildChannelSelection(), @@ -122,7 +150,7 @@ class _ChannelsScreenState extends State { Widget _buildAccountDetails() { if (_accountData == null) { - return const Center(child: CircularProgressIndicator(color: AppColors.primary)); + return const Center(child: CircularProgressIndicator(color: Colors.pinkAccent)); } return SingleChildScrollView( @@ -130,59 +158,180 @@ class _ChannelsScreenState extends State { child: Column( children: [ Container( - padding: const EdgeInsets.all(24), + padding: const EdgeInsets.all(32), decoration: BoxDecoration( - color: AppColors.cardBg, + gradient: LinearGradient( + colors: [ + AppColors.cardBg.withOpacity(0.9), + AppColors.cardBg.withOpacity(0.7), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), borderRadius: BorderRadius.circular(30), border: Border.all(color: Colors.white.withOpacity(0.1)), + boxShadow: [ + BoxShadow(color: const Color(0xFF833AB4).withOpacity(0.15), blurRadius: 30, offset: const Offset(0, 10)), + BoxShadow(color: Colors.black.withOpacity(0.3), blurRadius: 20, offset: const Offset(0, 10)), + ], ), child: Column( children: [ - CircleAvatar( - radius: 60, - backgroundImage: NetworkImage(_accountData!['profile_picture_url'] ?? ''), - ), - const SizedBox(height: 20), - Text( - _accountData!['username'] ?? 'User', - style: GoogleFonts.nunito( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, + Stack( + alignment: Alignment.center, children: [ - _buildStatItem(_accountData!['media_count'].toString(), "Posts"), - _buildStatItem(_accountData!['followers_count'].toString(), "Followers"), - _buildStatItem(_accountData!['follows_count'].toString(), "Following"), + Container( + width: 130, + height: 130, + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: const LinearGradient( + colors: [Color(0xFF833AB4), Color(0xFFFD1D1D), Color(0xFFFCAF45)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + boxShadow: [ + BoxShadow( + color: const Color(0xFFFD1D1D).withOpacity(0.4), + blurRadius: 20, + offset: const Offset(0, 8), + ), + ], + ), + ), + Container( + width: 122, + height: 122, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: AppColors.cardBg, // Border effect + ), + child: Padding( + padding: const EdgeInsets.all(4.0), + child: CircleAvatar( + radius: 56, + backgroundColor: Colors.black, + backgroundImage: NetworkImage(_accountData!['profile_picture_url'] ?? ''), + ), + ), + ), + Positioned( + bottom: 0, + right: 10, + child: Container( + padding: const EdgeInsets.all(6), + decoration: BoxDecoration( + color: const Color(0xFFFD1D1D), + shape: BoxShape.circle, + border: Border.all(color: AppColors.cardBg, width: 3), + ), + child: const Icon(Icons.check, color: Colors.white, size: 16), + ), + ), ], ), const SizedBox(height: 24), - if (_accountData!['biography'] != null) - Text( - _accountData!['biography'], - textAlign: TextAlign.center, - style: GoogleFonts.nunito(color: Colors.white70), + Text( + _accountData!['username'] ?? 'User', + style: GoogleFonts.nunito( + fontSize: 28, + fontWeight: FontWeight.w900, + color: Colors.white, + letterSpacing: 0.5, + ), + ), + if (_accountData!['name'] != null) + Padding( + padding: const EdgeInsets.only(top: 8), + child: Text( + _accountData!['name'], + style: GoogleFonts.nunito( + fontSize: 16, + color: Colors.white54, + fontWeight: FontWeight.w600, + ), + ), ), const SizedBox(height: 32), + Container( + padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16), + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.03), + borderRadius: BorderRadius.circular(20), + border: Border.all(color: Colors.white.withOpacity(0.05)), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + _buildStatItem(_accountData!['media_count']?.toString() ?? "0", "Posts"), + Container(width: 1, height: 40, color: Colors.white10), + _buildStatItem(_accountData!['followers_count']?.toString() ?? "0", "Followers"), + Container(width: 1, height: 40, color: Colors.white10), + _buildStatItem(_accountData!['follows_count']?.toString() ?? "0", "Following"), + ], + ), + ), + const SizedBox(height: 32), + if (_accountData!['biography'] != null) + Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.2), + borderRadius: BorderRadius.circular(16), + ), + child: Text( + _accountData!['biography'], + textAlign: TextAlign.center, + style: GoogleFonts.nunito(color: Colors.white70, fontSize: 14, height: 1.5), + ), + ), + const SizedBox(height: 40), SizedBox( width: double.infinity, + height: 56, child: ElevatedButton( onPressed: _removeChannel, style: ElevatedButton.styleFrom( - backgroundColor: Colors.red.shade700, - padding: const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + backgroundColor: Colors.red.withOpacity(0.1), + side: BorderSide(color: Colors.red.withOpacity(0.5), width: 1), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + elevation: 0, + ).copyWith( + overlayColor: MaterialStateProperty.all(Colors.red.withOpacity(0.1)), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.logout, color: Colors.red), + const SizedBox(width: 12), + Text( + "Disconnect Channel", + style: GoogleFonts.nunito( + color: Colors.red, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ], ), - child: const Text("Remove Channel"), ), ), ], ), ), + const SizedBox(height: 24), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.info_outline, color: Colors.white38, size: 16), + const SizedBox(width: 8), + Text( + "Switch channels by disconnecting current one", + textAlign: TextAlign.center, + style: GoogleFonts.nunito(color: Colors.white38, fontSize: 12), + ), + ], + ), ], ), ); @@ -191,47 +340,236 @@ class _ChannelsScreenState extends State { Widget _buildStatItem(String value, String label) { return Column( children: [ - Text(value, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white)), - Text(label, style: const TextStyle(fontSize: 12, color: Colors.white54)), + Text( + value, + style: GoogleFonts.nunito( + fontSize: 24, + fontWeight: FontWeight.w900, + color: Colors.white, + ), + ), + const SizedBox(height: 4), + Text( + label, + style: GoogleFonts.nunito( + fontSize: 12, + color: Colors.white54, + fontWeight: FontWeight.w600, + letterSpacing: 0.5, + ), + ), ], ); } Widget _buildChannelSelection() { + if (_channels.isEmpty) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient( + colors: [Colors.purple.withOpacity(0.2), Colors.pink.withOpacity(0.2)], + ), + ), + child: const Icon(Icons.account_circle_outlined, size: 64, color: Colors.white24), + ), + const SizedBox(height: 16), + Text( + "No Instagram Channels", + style: GoogleFonts.nunito( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: Text( + "Connect via Facebook to get started", + textAlign: TextAlign.center, + style: GoogleFonts.nunito( + color: Colors.white54, + fontSize: 13, + height: 1.5, + ), + ), + ), + ], + ), + ); + } + return ListView.builder( - padding: const EdgeInsets.all(24), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), itemCount: _channels.length, itemBuilder: (context, index) { final ch = _channels[index]; final hasLinked = ch['has_linked_account'] == true; + return Container( - margin: const EdgeInsets.only(bottom: 16), - padding: const EdgeInsets.all(20), + margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( - color: AppColors.cardBg, - borderRadius: BorderRadius.circular(20), - border: Border.all(color: Colors.white.withOpacity(0.1)), - ), - child: Column( - children: [ - Text( - ch['name'], - style: GoogleFonts.nunito(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white), - ), - const SizedBox(height: 16), - SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: hasLinked ? () => _connectChannel(ch['id'], ch['name']) : null, - style: ElevatedButton.styleFrom( - backgroundColor: hasLinked ? AppColors.primary : Colors.grey.shade800, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - ), - child: Text(hasLinked ? "Connect" : "Not Linked"), - ), + borderRadius: BorderRadius.circular(16), + gradient: LinearGradient( + colors: hasLinked + ? [Colors.purple.withOpacity(0.1), Colors.pink.withOpacity(0.1)] + : [Colors.grey.withOpacity(0.05), Colors.grey.withOpacity(0.05)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + border: Border.all( + width: 1, + color: hasLinked + ? Colors.purpleAccent.withOpacity(0.3) + : Colors.white.withOpacity(0.1), + ), + boxShadow: [ + BoxShadow( + color: hasLinked + ? Colors.purpleAccent.withOpacity(0.1) + : Colors.black.withOpacity(0.2), + blurRadius: 8, + offset: const Offset(0, 2), ), ], ), + child: Material( + color: Colors.transparent, + child: InkWell( + borderRadius: BorderRadius.circular(16), + onTap: hasLinked ? () => _connectChannel(ch['id'].toString(), ch['name']) : null, + child: Padding( + padding: const EdgeInsets.all(12), + child: Row( + children: [ + // Instagram Icon + Container( + width: 48, + height: 48, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + gradient: hasLinked + ? const LinearGradient( + colors: [Color(0xFF833AB4), Color(0xFFFD1D1D), Color(0xFFFCAF45)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ) + : LinearGradient( + colors: [Colors.grey.shade800, Colors.grey.shade700], + ), + boxShadow: [ + BoxShadow( + color: hasLinked + ? Colors.purpleAccent.withOpacity(0.2) + : Colors.black.withOpacity(0.1), + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], + ), + child: const Icon( + Icons.camera_alt_rounded, + color: Colors.white, + size: 24, + ), + ), + const SizedBox(width: 12), + + // Channel Info + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Flexible( + child: Text( + ch['name'] ?? 'Channel', + style: GoogleFonts.nunito( + fontSize: 15, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + overflow: TextOverflow.ellipsis, + ), + ), + if (hasLinked) ...[ + const SizedBox(width: 6), + Icon(Icons.check_circle, size: 14, color: Colors.greenAccent), + ], + ], + ), + const SizedBox(height: 4), + Text( + hasLinked ? "Ready" : "Auth Required", + style: GoogleFonts.nunito( + fontSize: 11, + color: hasLinked ? Colors.greenAccent : Colors.orangeAccent, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + + const SizedBox(width: 12), + + // Connect Button + Container( + height: 36, + decoration: BoxDecoration( + gradient: hasLinked + ? const LinearGradient( + colors: [Color(0xFF667EEA), Color(0xFF764BA2)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ) + : null, + color: hasLinked ? null : Colors.white.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Material( + color: Colors.transparent, + child: InkWell( + borderRadius: BorderRadius.circular(10), + onTap: hasLinked ? () => _connectChannel(ch['id'].toString(), ch['name']) : null, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + hasLinked ? "Link" : "Lock", + style: GoogleFonts.nunito( + color: hasLinked ? Colors.white : Colors.white38, + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ), + const SizedBox(width: 4), + Icon( + hasLinked ? Icons.arrow_forward_rounded : Icons.lock_outline, + color: hasLinked ? Colors.white : Colors.white38, + size: 14, + ), + ], + ), + ), + ), + ), + ), + ], + ), + ), + ), + ), ); }, ); diff --git a/lib/features/connect/screens/social_connect_details_screen.dart b/lib/features/connect/screens/social_connect_details_screen.dart index d9da557..aabf4be 100644 --- a/lib/features/connect/screens/social_connect_details_screen.dart +++ b/lib/features/connect/screens/social_connect_details_screen.dart @@ -5,6 +5,9 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../../core/constants/colors.dart'; import '../services/social_auth_service.dart'; +import 'channels_screen.dart'; +import '../../posts/screens/posts_screen.dart'; +import '../../automation/screens/automation_screen.dart'; class SocialMediaConnectScreen extends StatefulWidget { final bool isTab; @@ -224,7 +227,7 @@ class _SocialMediaConnectScreenState extends State wit // Disconnect Button if (_status == "connected") _buildFullWidthButton( - text: "Remove Facebook Account", + text: "Disconnect Facebook", onPressed: _isLoading ? null : _handleDisconnect, color: const Color(0xFFE11D48), ), @@ -264,16 +267,16 @@ class _SocialMediaConnectScreenState extends State wit Widget _buildGradientButton({required String text, required VoidCallback? onPressed, required List colors}) { return SizedBox( width: double.infinity, - height: 60, + height: 56, child: DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient(colors: colors), - borderRadius: BorderRadius.circular(50), + borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: colors.first.withOpacity(0.3), - blurRadius: 15, - offset: const Offset(0, 5), + blurRadius: 12, + offset: const Offset(0, 4), ), ], ), @@ -281,12 +284,16 @@ class _SocialMediaConnectScreenState extends State wit style: ElevatedButton.styleFrom( backgroundColor: Colors.transparent, shadowColor: Colors.transparent, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(50)), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + padding: const EdgeInsets.symmetric(horizontal: 24), ), onPressed: onPressed, - child: Text( - text, - style: GoogleFonts.nunito(fontSize: 18, fontWeight: FontWeight.w800, color: Colors.white), + child: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + text, + style: GoogleFonts.nunito(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white), + ), ), ), ), @@ -296,20 +303,26 @@ class _SocialMediaConnectScreenState extends State wit Widget _buildFullWidthButton({required String text, required VoidCallback? onPressed, required Color color}) { return SizedBox( width: double.infinity, - height: 60, + height: 56, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: color, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(50)), - elevation: 4, - shadowColor: color.withOpacity(0.4), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + elevation: 0, + padding: const EdgeInsets.symmetric(horizontal: 24), + shadowColor: Colors.transparent, + ).copyWith( + overlayColor: MaterialStateProperty.all(Colors.white.withOpacity(0.1)), ), onPressed: onPressed, child: _isLoading ? const SizedBox(height: 24, width: 24, child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2)) - : Text( - text, - style: GoogleFonts.nunito(fontSize: 18, fontWeight: FontWeight.w800, color: Colors.white), + : FittedBox( + fit: BoxFit.scaleDown, + child: Text( + text, + style: GoogleFonts.nunito(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white), + ), ), ), ); diff --git a/lib/features/home/screens/home_screen.dart b/lib/features/home/screens/home_screen.dart index 0176b00..39f127f 100644 --- a/lib/features/home/screens/home_screen.dart +++ b/lib/features/home/screens/home_screen.dart @@ -10,6 +10,7 @@ import 'package:socialbuddy_mobile/features/automation/screens/automation_screen import 'package:socialbuddy_mobile/features/connect/screens/channels_screen.dart'; import 'package:socialbuddy_mobile/features/profile/screens/profile_screen.dart'; +import 'package:socialbuddy_mobile/features/profile/screens/pricing_screen.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @@ -23,8 +24,10 @@ class _HomeScreenState extends State { static final List _widgetOptions = [ const DashboardTab(), - const PostsScreen(), const SocialMediaConnectScreen(), + const ChannelsScreen(), + const PostsScreen(), + const AutomationScreen(), const ProfileScreen(), ]; @@ -46,17 +49,6 @@ class _HomeScreenState extends State { child: Image.asset('assets/images/logo_sb.png', errorBuilder: (c, e, s) => const Icon(Icons.hub, color: Colors.blue)), ), actions: [ - IconButton( - icon: Container( - padding: const EdgeInsets.all(6), - decoration: BoxDecoration( - color: Colors.white.withOpacity(0.05), - shape: BoxShape.circle, - ), - child: const Icon(Icons.grid_view_rounded, color: Colors.white70, size: 20), - ), - onPressed: () {}, - ), IconButton( icon: Container( padding: const EdgeInsets.all(6), @@ -66,7 +58,7 @@ class _HomeScreenState extends State { ), child: const Icon(Icons.person_outline, color: Colors.white70, size: 20), ), - onPressed: () => _onItemTapped(3), + onPressed: () => _onItemTapped(5), // Navigate to Profile (index 5) ), const SizedBox(width: 8), ], @@ -83,14 +75,25 @@ class _HomeScreenState extends State { activeIcon: Icon(Icons.grid_view), label: 'Home', ), + BottomNavigationBarItem( + icon: Icon(Icons.link), + activeIcon: Icon(Icons.link), + label: 'Connect', + ), + BottomNavigationBarItem( + icon: Icon(Icons.account_circle_outlined), + activeIcon: Icon(Icons.account_circle), + label: 'Channels', + ), BottomNavigationBarItem( icon: Icon(Icons.post_add_outlined), activeIcon: Icon(Icons.post_add), label: 'Posts', ), BottomNavigationBarItem( - icon: Icon(Icons.link), - label: 'Connect', + icon: Icon(Icons.comment_outlined), + activeIcon: Icon(Icons.comment), + label: 'Automation', ), BottomNavigationBarItem( icon: Icon(Icons.person_outline), @@ -127,10 +130,10 @@ class DashboardTab extends StatelessWidget { if (role == 'customer') { final session = prefs.getString('payment_session'); if (session == null) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text("Pricing check required. Please subscribe.")), + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const PricingScreen()), ); - // In a real app, you would navigate to PricingScreen return; } } @@ -205,14 +208,21 @@ class DashboardTab extends StatelessWidget { description: "Automatically reply to comments/messages using smart automation.", icon: Icons.chat_bubble_outline_rounded, iconColor: Colors.blueAccent, - onTap: () {}, // Tab removed in 4-tab layout + onTap: () => _checkAndNavigate(context, const AutomationScreen()), + ), + FeatureCard( + title: "Instagram Channels", + description: "Select and connect specific Instagram business accounts to manage.", + icon: Icons.account_tree_outlined, + iconColor: Colors.pinkAccent, + onTap: () => _checkAndNavigate(context, const ChannelsScreen()), ), FeatureCard( title: "Instagram Media", description: "View all Instagram media from your connected accounts with insights.", icon: Icons.camera_alt_outlined, - iconColor: Colors.pinkAccent, - onTap: () => homeState?._onItemTapped(2), + iconColor: Colors.cyanAccent, + onTap: () => homeState?._onItemTapped(1), ), FeatureCard( title: "Social Media Posts", @@ -226,21 +236,33 @@ class DashboardTab extends StatelessWidget { description: "Track auto reply logs and automation actions with timestamps.", icon: Icons.history_rounded, iconColor: Colors.orangeAccent, - onTap: () {}, + onTap: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Automation Logs coming soon!")), + ); + }, ), FeatureCard( title: "Users", description: "Manage users, roles, and access permissions.", icon: Icons.person_search_outlined, iconColor: Colors.purpleAccent, - onTap: () {}, + onTap: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("User Management coming soon!")), + ); + }, ), FeatureCard( title: "Reports", description: "Analyze insights, performance charts, and engagement metrics.", icon: Icons.auto_graph_rounded, iconColor: Colors.cyanAccent, - onTap: () {}, + onTap: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Reports and Analytics coming soon!")), + ); + }, ), const SizedBox(height: 40), Center( diff --git a/lib/features/posts/screens/posts_screen.dart b/lib/features/posts/screens/posts_screen.dart index 33820c5..739d94a 100644 --- a/lib/features/posts/screens/posts_screen.dart +++ b/lib/features/posts/screens/posts_screen.dart @@ -1,117 +1,357 @@ +import 'dart:convert'; +import 'dart:ui'; + import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:socialbuddy_mobile/core/constants/colors.dart'; +import 'package:socialbuddy_mobile/core/constants/api_constants.dart'; +import '../../connect/screens/channels_screen.dart'; +import '../../connect/screens/social_connect_details_screen.dart'; +import '../../automation/screens/automation_screen.dart'; +import '../../../core/constants/colors.dart'; -class PostsScreen extends StatelessWidget { +class PostsScreen extends StatefulWidget { const PostsScreen({super.key}); @override - Widget build(BuildContext context) { - // Dummy posts data - final List> posts = List.generate(10, (index) => { - 'image': 'https://picsum.photos/500/500?random=$index', - 'caption': 'This is a sample post caption for Social Buddy #social #marketing', - 'likes': '${(index + 1) * 12}', - 'comments': '${index * 3}', - 'type': index % 3 == 0 ? 'VIDEO' : 'IMAGE', + State createState() => _PostsScreenState(); +} + +class _PostsScreenState extends State { + List _media = []; + bool _isLoading = true; + String? _error; + String? _connectedChannelName; + Map? _accountData; + + @override + void initState() { + super.initState(); + _fetchMedia(); + } + + Future _fetchMedia() async { + if (!mounted) return; + + setState(() { + _isLoading = true; + _error = null; }); + try { + final prefs = await SharedPreferences.getInstance(); + final userEmail = prefs.getString('user_email'); + final connectedChannelId = prefs.getString('connectedChannel'); + + if (userEmail == null) { + setState(() { + _error = "User email not found. Please log in again."; + _isLoading = false; + }); + return; + } + + // Check if we have account details saved or fetch them + if (connectedChannelId != null) { + await _fetchAccountDetails(userEmail); + } + + final url = Uri.parse('${ApiConstants.liveBaseUrl}/social/media?userId=$userEmail&limit=20'); + final response = await http.get(url); + + if (!mounted) return; + + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + setState(() { + _media = data['data'] ?? []; + _isLoading = false; + }); + } else { + final data = jsonDecode(response.body); + setState(() { + // Backend might return 'error' or 'message' + _error = data['error'] ?? data['message'] ?? "Failed to load media"; + _isLoading = false; + }); + } + } catch (e) { + if (mounted) { + setState(() { + _error = "An error occurred: $e"; + _isLoading = false; + }); + } + } + } + + Future _fetchAccountDetails(String email) async { + try { + final response = await http.get( + Uri.parse('${ApiConstants.liveBaseUrl}/social/account?userId=$email'), + ); + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + setState(() { + _accountData = data; + _connectedChannelName = data['username']; + }); + } + } catch (e) { + debugPrint("Error fetching account for posts header: $e"); + } + } + + @override + Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.darkBg, - body: GridView.builder( - padding: const EdgeInsets.all(16), - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - childAspectRatio: 0.65, - crossAxisSpacing: 16, - mainAxisSpacing: 16, + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Media Library", style: GoogleFonts.nunito(fontWeight: FontWeight.bold)), + if (_connectedChannelName != null) + Text("Account: @$_connectedChannelName", + style: GoogleFonts.nunito(fontSize: 12, color: Colors.white54)), + ], ), - itemCount: posts.length, - itemBuilder: (context, index) { - final post = posts[index]; - return Container( - decoration: BoxDecoration( - color: AppColors.cardBg, - borderRadius: BorderRadius.circular(16), - border: Border.all(color: Colors.white.withOpacity(0.05)), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Stack( - children: [ - ClipRRect( - borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), - child: Image.network( - post['image']!, - fit: BoxFit.cover, - width: double.infinity, - height: double.infinity, - ), - ), - Positioned( - top: 8, - right: 8, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), - decoration: BoxDecoration( - color: Colors.black54, - borderRadius: BorderRadius.circular(4), - ), - child: Text( - post['type']!, - style: const TextStyle(color: Colors.white, fontSize: 8), - ), - ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - post['caption']!, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: GoogleFonts.nunito(fontSize: 12, color: Colors.white), - ), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - const Icon(Icons.favorite, size: 14, color: AppColors.danger), - const SizedBox(width: 4), - Text(post['likes']!, style: const TextStyle(fontSize: 10, color: Colors.white70)), - ], - ), - Row( - children: [ - const Icon(Icons.comment, size: 14, color: Colors.blueAccent), - const SizedBox(width: 4), - Text(post['comments']!, style: const TextStyle(fontSize: 10, color: Colors.white70)), - ], - ), - ], - ), - ], - ), - ), - ], - ), - ); - }, + actions: [ + IconButton( + icon: const Icon(Icons.refresh), + onPressed: _fetchMedia, + ), + ], + ), + body: RefreshIndicator( + onRefresh: _fetchMedia, + color: Colors.pinkAccent, + child: _buildBody(), ), floatingActionButton: FloatingActionButton( - onPressed: () {}, - backgroundColor: AppColors.primary, + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Create Post feature coming soon!")), + ); + }, + backgroundColor: Colors.blueAccent, child: const Icon(Icons.add, color: Colors.white), ), ); } + + Widget _buildBody() { + if (_isLoading) { + return const Center(child: CircularProgressIndicator(color: Colors.pinkAccent)); + } + + if (_error != null) { + return SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: Container( + height: MediaQuery.of(context).size.height * 0.7, + alignment: Alignment.center, + padding: const EdgeInsets.all(24), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.error_outline, size: 64, color: Colors.orangeAccent), + const SizedBox(height: 16), + Text( + _error!, + textAlign: TextAlign.center, + style: const TextStyle(color: Colors.white70, fontSize: 16), + ), + const SizedBox(height: 24), + if (_error!.contains("Connect a channel")) + ElevatedButton( + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) => const ChannelsScreen()), + ).then((_) => _fetchMedia()), + style: ElevatedButton.styleFrom(backgroundColor: Colors.pinkAccent), + child: const Text("Go to Channels"), + ) + else + ElevatedButton( + onPressed: _fetchMedia, + style: ElevatedButton.styleFrom(backgroundColor: Colors.blueAccent), + child: const Text("Retry"), + ), + ], + ), + ), + ); + } + + if (_media.isEmpty) { + return SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: Container( + height: MediaQuery.of(context).size.height * 0.7, + alignment: Alignment.center, + padding: const EdgeInsets.all(24), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.photo_library_outlined, size: 80, color: Colors.white10), + const SizedBox(height: 24), + Text( + "No Instagram media found.", + textAlign: TextAlign.center, + style: GoogleFonts.nunito(fontSize: 20, color: Colors.white, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8), + const Text( + "Make sure you have an active Instagram business account connected.", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.white38), + ), + const SizedBox(height: 32), + ElevatedButton( + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) => const ChannelsScreen()), + ).then((_) => _fetchMedia()), + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.primary, + padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + ), + child: const Text("Connect Channel"), + ), + ], + ), + ), + ); + } + + return GridView.builder( + padding: const EdgeInsets.all(16), + physics: const AlwaysScrollableScrollPhysics(), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: 0.65, + crossAxisSpacing: 16, + mainAxisSpacing: 16, + ), + itemCount: _media.length, + itemBuilder: (context, index) { + final item = _media[index]; + final mediaType = item['media_type']; + final mediaUrl = item['media_url']; + final caption = item['caption'] ?? ""; + final likes = item['like_count']?.toString() ?? "0"; + final comments = item['comments_count']?.toString() ?? "0"; + + return Container( + decoration: BoxDecoration( + color: AppColors.cardBg, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: Colors.white.withOpacity(0.05)), + boxShadow: [ + BoxShadow(color: Colors.black26, blurRadius: 10, offset: const Offset(0, 5)), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Stack( + children: [ + ClipRRect( + borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), + child: Image.network( + mediaUrl, + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) return child; + return Container( + color: Colors.white.withOpacity(0.02), + child: const Center(child: CircularProgressIndicator(strokeWidth: 2)), + ); + }, + errorBuilder: (c, e, s) => Container( + color: Colors.white10, + child: const Center(child: Icon(Icons.broken_image, color: Colors.white24, size: 32)), + ), + ), + ), + Positioned( + top: 8, + right: 8, + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: Colors.black54.withOpacity(0.6), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + _getMediaTypeName(mediaType), + style: const TextStyle(color: Colors.white, fontSize: 8, fontWeight: FontWeight.bold), + ), + ), + ), + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + caption, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: GoogleFonts.nunito(fontSize: 12, color: Colors.white, height: 1.3), + ), + const SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _buildStatChip(Icons.favorite, likes, Colors.pinkAccent), + _buildStatChip(Icons.comment, comments, Colors.blueAccent), + ], + ), + ], + ), + ), + ], + ), + ); + }, + ); + } + + String _getMediaTypeName(String type) { + switch (type) { + case 'IMAGE': return 'PHOTO'; + case 'VIDEO': return 'VIDEO'; + case 'CAROUSEL_ALBUM': return 'CAROUSEL'; + default: return type; + } + } + + Widget _buildStatChip(IconData icon, String value, Color color) { + return Row( + children: [ + Icon(icon, size: 14, color: color), + const SizedBox(width: 4), + Text(value, style: const TextStyle(fontSize: 10, color: Colors.white70, fontWeight: FontWeight.bold)), + ], + ); + } } diff --git a/lib/features/profile/screens/pricing_screen.dart b/lib/features/profile/screens/pricing_screen.dart new file mode 100644 index 0000000..00f2921 --- /dev/null +++ b/lib/features/profile/screens/pricing_screen.dart @@ -0,0 +1,306 @@ +import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class PricingScreen extends StatefulWidget { + const PricingScreen({super.key}); + + @override + State createState() => _PricingScreenState(); +} + +class _PricingScreenState extends State { + bool _isYearly = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFF0A0B17), + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + title: Text("Pricing Plans", + style: GoogleFonts.nunito(color: Colors.white, fontWeight: FontWeight.bold)), + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () => Navigator.pop(context), + ), + ), + body: Stack( + children: [ + // Background Glows + Positioned(top: 100, left: -50, child: _buildGlowBlob(const Color(0xFF1D8BE0), 150)), + Positioned(bottom: 50, right: -50, child: _buildGlowBlob(const Color(0xFFEC4899), 150)), + + SingleChildScrollView( + padding: const EdgeInsets.all(24), + child: Column( + children: [ + Text( + "Choose Your Plan", + style: GoogleFonts.nunito( + color: Colors.white, + fontSize: 32, + fontWeight: FontWeight.w800, + ), + ), + const SizedBox(height: 8), + Text( + "Unlock premium features and grow your social presence.", + textAlign: TextAlign.center, + style: GoogleFonts.nunito( + color: Colors.white.withOpacity(0.6), + fontSize: 16, + ), + ), + const SizedBox(height: 32), + + // Billing Toggle + Container( + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.05), + borderRadius: BorderRadius.circular(50), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + _buildToggleButton("Monthly", !_isYearly), + _buildToggleButton("Yearly (Save 20%)", _isYearly), + ], + ), + ), + const SizedBox(height: 40), + + // Free Trial + _buildPricingCard( + title: "7-Day Free Trial", + price: "\$0", + duration: "7 Days", + desc: "Experience SocialBuddy with no commitment.", + features: ["Full Platform Access", "All Premium Features", "No Credits Required", "Cancel Anytime"], + isPopular: false, + color: Colors.cyanAccent, + buttonText: "START FREE TRIAL", + ), + const SizedBox(height: 24), + + // Basic Buddy + _buildPricingCard( + title: "Basic Buddy", + price: _isYearly ? "\$100" : "\$10", + duration: _isYearly ? "Year" : "Month", + desc: "Perfect for individual creators.", + features: ["1 Social Profile", "Basic Analytics", "Content Scheduling", "Standard Support"], + isPopular: false, + color: Colors.blueAccent, + ), + const SizedBox(height: 24), + + // Standard Buddy + _buildPricingCard( + title: "Standard Buddy", + price: _isYearly ? "\$500" : "\$50", + duration: _isYearly ? "Year" : "Month", + desc: "For growing businesses and influencers.", + features: ["10 Social Profiles", "Advanced Analytics", "AI Content Generation", "Priority Support", "Team Collaboration (3)"], + isPopular: true, + color: const Color(0xFFEC4899), + ), + const SizedBox(height: 24), + + // Premium Buddy + _buildPricingCard( + title: "Premium Buddy", + price: _isYearly ? "\$1500" : "\$150", + duration: _isYearly ? "Year" : "Month", + desc: "Ultimate solution for agencies.", + features: ["25 Social Profiles", "White Label Reports", "Dedicated Account Manager", "API Access", "Unlimited Team Members"], + isPopular: false, + color: Colors.purpleAccent, + ), + const SizedBox(height: 40), + ], + ), + ), + ], + ), + ); + } + + Widget _buildToggleButton(String label, bool isActive) { + return GestureDetector( + onTap: () => setState(() => _isYearly = label.contains("Yearly")), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50), + gradient: isActive + ? const LinearGradient(colors: [Color(0xFF4361EE), Color(0xFFC026D3)]) + : null, + color: isActive ? null : Colors.transparent, + ), + child: Text( + label, + style: GoogleFonts.nunito( + color: isActive ? Colors.white : Colors.white38, + fontWeight: FontWeight.bold, + fontSize: 14, + ), + ), + ), + ); + } + + Widget _buildPricingCard({ + required String title, + required String price, + required String duration, + required String desc, + required List features, + required bool isPopular, + required Color color, + String buttonText = "GET STARTED", + }) { + return Container( + width: double.infinity, + decoration: BoxDecoration( + color: const Color(0xFF1A1D26).withOpacity(0.8), + borderRadius: BorderRadius.circular(24), + border: Border.all( + color: isPopular ? color : Colors.white.withOpacity(0.1), + width: isPopular ? 2 : 1, + ), + ), + child: Stack( + children: [ + if (isPopular) + Positioned( + top: 0, + right: 0, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), + decoration: BoxDecoration( + color: color, + borderRadius: const BorderRadius.only( + topRight: Radius.circular(22), + bottomLeft: Radius.circular(24), + ), + ), + child: const Text( + "POPULAR", + style: TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(28), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: GoogleFonts.nunito( + color: Colors.white, + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 8), + Text( + desc, + style: GoogleFonts.nunito(color: Colors.white54, fontSize: 14), + ), + const SizedBox(height: 24), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + price, + style: GoogleFonts.nunito( + color: Colors.white, + fontSize: 40, + fontWeight: FontWeight.w800, + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 8, left: 4), + child: Text( + "/$duration", + style: TextStyle(color: Colors.white.withOpacity(0.5), fontSize: 16), + ), + ), + ], + ), + const SizedBox(height: 24), + const Divider(color: Colors.white10), + const SizedBox(height: 24), + ...features.map((f) => Padding( + padding: const EdgeInsets.only(bottom: 12), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(2), + decoration: BoxDecoration( + color: color.withOpacity(0.1), + shape: BoxShape.circle, + ), + child: Icon(Icons.check, color: color, size: 16), + ), + const SizedBox(width: 12), + Expanded( + child: Text(f, style: const TextStyle(color: Colors.white70, fontSize: 14)), + ), + ], + ), + )), + const SizedBox(height: 32), + SizedBox( + width: double.infinity, + height: 56, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient(colors: [color, color.withOpacity(0.7)]), + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow(color: color.withOpacity(0.3), blurRadius: 10, offset: const Offset(0, 4)), + ], + ), + child: ElevatedButton( + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Subscription to $title coming soon!")), + ); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.transparent, + shadowColor: Colors.transparent, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + ), + child: Text(buttonText, + style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 16)), + ), + ), + ) + ], + ), + ), + ], + ), + ); + } + + Widget _buildGlowBlob(Color color, double size) { + return Container( + width: size, + height: size, + decoration: BoxDecoration( + color: color.withOpacity(0.2), + shape: BoxShape.circle, + boxShadow: [ + BoxShadow(color: color.withOpacity(0.4), blurRadius: 100, spreadRadius: 10), + ], + ), + ); + } +}