import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import '../../controllers/add_subscription_controller.dart'; import '../../providers/category_provider.dart'; import '../common/form_fields/base_text_field.dart'; import '../common/form_fields/currency_input_field.dart'; import '../common/form_fields/date_picker_field.dart'; /// 구독 추가 화면의 폼 섹션 class AddSubscriptionForm extends StatelessWidget { final AddSubscriptionController controller; final Animation fadeAnimation; final Animation slideAnimation; final Function setState; const AddSubscriptionForm({ super.key, required this.controller, required this.fadeAnimation, required this.slideAnimation, required this.setState, }); @override Widget build(BuildContext context) { return FadeTransition( opacity: Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: controller.animationController!, curve: const Interval(0.2, 1.0, curve: Curves.easeIn), ), ), child: SlideTransition( position: Tween( begin: const Offset(0.0, 0.4), end: Offset.zero, ).animate(CurvedAnimation( parent: controller.animationController!, curve: const Interval(0.2, 1.0, curve: Curves.easeOutCubic), )), child: Card( elevation: 1, shadowColor: Colors.black12, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), child: Padding( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 헤더 Row( children: [ ShaderMask( shaderCallback: (bounds) => LinearGradient( colors: controller.gradientColors, begin: Alignment.topLeft, end: Alignment.bottomRight, ).createShader(bounds), child: const Icon( FontAwesomeIcons.fileLines, size: 20, color: Colors.white, ), ), const SizedBox(width: 12), const Text( '서비스 정보', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, letterSpacing: -0.5, color: Color(0xFF1E293B), ), ), ], ), const SizedBox(height: 24), // 서비스명 필드 BaseTextField( controller: controller.serviceNameController, focusNode: controller.serviceNameFocus, label: '서비스명', hintText: '예: Netflix, Spotify', textInputAction: TextInputAction.next, onEditingComplete: () { controller.monthlyCostFocus.requestFocus(); }, validator: (value) { if (value == null || value.isEmpty) { return '서비스명을 입력해주세요'; } return null; }, ), const SizedBox(height: 20), // 월 지출 및 통화 선택 Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 2, child: CurrencyInputField( controller: controller.monthlyCostController, currency: controller.currency, label: '월 지출', focusNode: controller.monthlyCostFocus, textInputAction: TextInputAction.next, onEditingComplete: () { controller.billingCycleFocus.requestFocus(); }, validator: (value) { if (value == null || value.isEmpty) { return '금액을 입력해주세요'; } return null; }, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '통화', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 8), _CurrencySelector( currency: controller.currency, onChanged: (value) { setState(() { controller.currency = value; }); }, ), ], ), ), ], ), const SizedBox(height: 20), // 결제 주기 Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '결제 주기', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 8), _BillingCycleSelector( billingCycle: controller.billingCycle, gradientColors: controller.gradientColors, onChanged: (value) { setState(() { controller.billingCycle = value; }); }, ), ], ), const SizedBox(height: 20), // 다음 결제일 DatePickerField( selectedDate: controller.nextBillingDate ?? DateTime.now(), onDateSelected: (date) { setState(() { controller.nextBillingDate = date; }); }, label: '다음 결제일', firstDate: DateTime.now(), lastDate: DateTime.now().add(const Duration(days: 365 * 2)), primaryColor: controller.gradientColors[0], ), const SizedBox(height: 20), // 웹사이트 URL BaseTextField( controller: controller.websiteUrlController, focusNode: controller.websiteUrlFocus, label: '웹사이트 URL (선택)', hintText: 'https://example.com', keyboardType: TextInputType.url, prefixIcon: Icon( Icons.link_rounded, color: Colors.grey[600], ), ), const SizedBox(height: 20), // 카테고리 선택 Consumer( builder: (context, categoryProvider, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '카테고리', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 8), _CategorySelector( categories: categoryProvider.categories, selectedCategoryId: controller.selectedCategoryId, gradientColors: controller.gradientColors, onChanged: (categoryId) { setState(() { controller.selectedCategoryId = categoryId; }); }, ), ], ); }, ), ], ), ), ), ), ); } } /// 통화 선택기 class _CurrencySelector extends StatelessWidget { final String currency; final ValueChanged onChanged; const _CurrencySelector({ required this.currency, required this.onChanged, }); @override Widget build(BuildContext context) { return Row( children: [ _CurrencyOption( label: '₩', value: 'KRW', isSelected: currency == 'KRW', onTap: () => onChanged('KRW'), ), const SizedBox(width: 8), _CurrencyOption( label: '\$', value: 'USD', isSelected: currency == 'USD', onTap: () => onChanged('USD'), ), ], ); } } /// 통화 옵션 class _CurrencyOption extends StatelessWidget { final String label; final String value; final bool isSelected; final VoidCallback onTap; const _CurrencyOption({ required this.label, required this.value, required this.isSelected, required this.onTap, }); @override Widget build(BuildContext context) { return Expanded( child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: isSelected ? const Color(0xFF3B82F6) : Colors.grey.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), ), child: Center( child: Text( label, style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: isSelected ? Colors.white : Colors.grey[600], ), ), ), ), ), ); } } /// 결제 주기 선택기 class _BillingCycleSelector extends StatelessWidget { final String billingCycle; final List gradientColors; final ValueChanged onChanged; const _BillingCycleSelector({ required this.billingCycle, required this.gradientColors, required this.onChanged, }); @override Widget build(BuildContext context) { final cycles = ['월간', '분기별', '반기별', '연간']; return SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: cycles.map((cycle) { final isSelected = billingCycle == cycle; return Padding( padding: const EdgeInsets.only(right: 8), child: InkWell( onTap: () => onChanged(cycle), borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 12, ), decoration: BoxDecoration( color: isSelected ? gradientColors[0] : Colors.grey.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), ), child: Text( cycle, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: isSelected ? Colors.white : Colors.grey[700], ), ), ), ), ); }).toList(), ), ); } } /// 카테고리 선택기 class _CategorySelector extends StatelessWidget { final List categories; final String? selectedCategoryId; final List gradientColors; final ValueChanged onChanged; const _CategorySelector({ required this.categories, required this.selectedCategoryId, required this.gradientColors, required this.onChanged, }); @override Widget build(BuildContext context) { return Wrap( spacing: 8, runSpacing: 8, children: categories.map((category) { final isSelected = selectedCategoryId == category.id; return InkWell( onTap: () => onChanged(category.id), borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 10, ), decoration: BoxDecoration( color: isSelected ? gradientColors[0] : Colors.grey.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( category.icon, style: const TextStyle(fontSize: 16), ), const SizedBox(width: 6), Text( category.name, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: isSelected ? Colors.white : Colors.grey[700], ), ), ], ), ), ); }).toList(), ); } }