import 'package:flutter/material.dart'; import '../../core/theme/app_colors.dart'; import '../../core/theme/app_decorations.dart'; import '../../core/theme/app_text_styles.dart'; import 'animations/animations.dart'; class FeaturePage extends StatelessWidget { final String title; final String subtitle; final Widget child; final Widget? trailing; const FeaturePage({ super.key, required this.title, required this.subtitle, required this.child, this.trailing, }); @override Widget build(BuildContext context) { return DecoratedBox( decoration: const BoxDecoration( gradient: LinearGradient( colors: [AppColors.softBlueBg, Colors.white], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: SafeArea( child: LayoutBuilder( builder: (context, constraints) { final short = constraints.maxHeight < 520; final compact = constraints.maxWidth < 420 || short; final wide = constraints.maxWidth >= 900; final horizontal = compact ? 12.0 : 20.0; return FadeSlideWrapper( child: Padding( padding: EdgeInsets.fromLTRB( horizontal, short ? 8 : 12, horizontal, short ? 10 : 14, ), child: Center( child: ConstrainedBox( constraints: BoxConstraints( maxWidth: wide ? 1160 : double.infinity, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ compact ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _FeatureHeading( title: title, subtitle: subtitle, compact: compact, ), if (trailing != null) ...[ const SizedBox(height: 10), Align( alignment: Alignment.centerLeft, child: trailing!, ), ], ], ) : Row( children: [ Expanded( child: _FeatureHeading( title: title, subtitle: subtitle, compact: compact, ), ), if (trailing != null) trailing!, ], ), SizedBox(height: short ? 8 : (compact ? 12 : 16)), Expanded( child: child, ), ], ), ), ), ), ); }, ), ), ); } } class _FeatureHeading extends StatelessWidget { final String title; final String subtitle; final bool compact; const _FeatureHeading({ required this.title, required this.subtitle, required this.compact, }); @override Widget build(BuildContext context) { final short = MediaQuery.sizeOf(context).height < 520; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, maxLines: short ? 1 : 2, overflow: TextOverflow.ellipsis, style: AppTextStyles.heading.copyWith( fontSize: compact ? 22 : null, ), ), const SizedBox(height: 2), Text( subtitle, maxLines: short ? 1 : (compact ? 2 : 3), overflow: TextOverflow.ellipsis, style: AppTextStyles.body.copyWith( color: AppColors.textMuted, fontWeight: FontWeight.w500, ), ), ], ); } } class FeatureEmptyPanel extends StatelessWidget { final IconData icon; final String title; final String message; final Widget? action; const FeatureEmptyPanel({ super.key, required this.icon, required this.title, required this.message, this.action, }); @override Widget build(BuildContext context) { return Center( child: Container( constraints: const BoxConstraints(maxWidth: 380), padding: const EdgeInsets.all(24), decoration: AppDecorations.card, child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 72, height: 72, decoration: BoxDecoration( color: AppColors.primary.withValues(alpha: 0.08), borderRadius: BorderRadius.circular(50), border: Border.all(color: AppColors.border), ), child: Icon(icon, size: 36, color: AppColors.primary), ), const SizedBox(height: 12), Text( title, textAlign: TextAlign.center, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w800), ), const SizedBox(height: 6), Text( message, textAlign: TextAlign.center, style: const TextStyle(color: AppColors.muted, height: 1.35), ), if (action != null) ...[ const SizedBox(height: 16), action!, ], ], ), ), ); } } class FeatureErrorPanel extends StatelessWidget { final String message; final VoidCallback? onRetry; const FeatureErrorPanel({ super.key, required this.message, this.onRetry, }); @override Widget build(BuildContext context) { return Center( child: Container( width: double.infinity, constraints: const BoxConstraints(maxWidth: 420), padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: const Color(0xFFFEF2F2), borderRadius: AppDecorations.cardRadius, border: Border.all(color: const Color(0xFFFECACA)), boxShadow: AppDecorations.cardShadow, ), child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.error_outline, color: Color(0xFFDC2626), size: 34), const SizedBox(height: 10), Text( message, textAlign: TextAlign.center, style: const TextStyle(color: Color(0xFF991B1B), height: 1.35), ), if (onRetry != null) ...[ const SizedBox(height: 12), FilledButton.icon( onPressed: onRetry, icon: const Icon(Icons.refresh), label: const Text('Coba lagi'), ), ], ], ), ), ); } }