import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../controllers/detail_screen_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 DetailFormSection extends StatelessWidget { final DetailScreenController controller; final Animation fadeAnimation; final Animation slideAnimation; const DetailFormSection({ super.key, required this.controller, required this.fadeAnimation, required this.slideAnimation, }); @override Widget build(BuildContext context) { final baseColor = controller.getCardColor(); return FadeTransition( opacity: fadeAnimation, child: SlideTransition( position: Tween( begin: const Offset(0.0, 0.6), end: Offset.zero, ).animate(CurvedAnimation( parent: controller.animationController!, curve: const Interval(0.3, 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, mainAxisSize: MainAxisSize.min, children: [ // 서비스명 필드 BaseTextField( controller: controller.serviceNameController, focusNode: controller.serviceNameFocus, label: '서비스명', hintText: '예: Netflix, Spotify', textInputAction: TextInputAction.next, onEditingComplete: () { controller.monthlyCostFocus.requestFocus(); }, ), 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(); }, ), ), 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) { controller.currency = value; // 통화 변경시 금액 포맷 업데이트 if (value == 'KRW') { final amount = double.tryParse( controller.monthlyCostController.text.replaceAll(',', '') ); if (amount != null) { controller.monthlyCostController.text = amount.toInt().toString(); } } }, ), ], ), ), ], ), 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, baseColor: baseColor, onChanged: (value) { controller.billingCycle = value; }, ), ], ), const SizedBox(height: 20), // 다음 결제일 DatePickerField( selectedDate: controller.nextBillingDate, onDateSelected: (date) { controller.nextBillingDate = date; }, label: '다음 결제일', firstDate: DateTime.now(), lastDate: DateTime.now().add(const Duration(days: 365 * 2)), primaryColor: baseColor, ), 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, baseColor: baseColor, onChanged: (categoryId) { 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 ? Theme.of(context).primaryColor : Colors.grey.withOpacity(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 Color baseColor; final ValueChanged onChanged; const _BillingCycleSelector({ required this.billingCycle, required this.baseColor, 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 ? baseColor : Colors.grey.withOpacity(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 Color baseColor; final ValueChanged onChanged; const _CategorySelector({ required this.categories, required this.selectedCategoryId, required this.baseColor, 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 ? baseColor : Colors.grey.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( category.emoji, 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(), ); } }