import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:lucide_icons/lucide_icons.dart'; import '../../../models/sake_item.dart'; import '../../../models/user_profile.dart'; import '../../../providers/theme_provider.dart'; import '../../../services/pricing_calculator.dart'; import '../../../theme/app_colors.dart'; /// Business pricing section for sake detail screen. /// Displays pricing info and provides price editing dialog. class SakePricingSection extends ConsumerWidget { final SakeItem sake; final ValueChanged onUpdated; const SakePricingSection({ super.key, required this.sake, required this.onUpdated, }); @override Widget build(BuildContext context, WidgetRef ref) { final userProfile = ref.watch(userProfileProvider); if (!userProfile.isBusinessMode) return const SizedBox.shrink(); return _buildPricingContent(context, userProfile); } Widget _buildPricingContent(BuildContext context, UserProfile userProfile) { final appColors = Theme.of(context).extension()!; final calculatedPrice = PricingCalculator.calculatePrice(sake); return Container( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Theme.of(context).primaryColor.withValues(alpha: 0.05), borderRadius: BorderRadius.circular(12), border: Border.all(color: Theme.of(context).primaryColor.withValues(alpha: 0.2)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(LucideIcons.coins, color: appColors.brandPrimary, size: 18), const SizedBox(width: 6), Text( '価格設定', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: appColors.brandPrimary), ), ], ), const SizedBox(height: 4), Text( calculatedPrice > 0 ? '現在$calculatedPrice円' : '未設定', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: calculatedPrice > 0 ? appColors.brandPrimary : appColors.textTertiary, ), ), ], ), ElevatedButton( onPressed: () => _showPriceSettingsDialog(context, userProfile), style: ElevatedButton.styleFrom( backgroundColor: appColors.brandPrimary, foregroundColor: Theme.of(context).colorScheme.onPrimary, ), child: const Text('編集'), ), ], ), ); } Future _showPriceSettingsDialog(BuildContext context, UserProfile argProfile) async { if (!argProfile.isBusinessMode) return; int? cost = sake.userData.costPrice; int? manual = sake.userData.price; double markup = sake.userData.markup; Map variants = Map.from(sake.userData.priceVariants ?? {}); String tempName = ''; String tempPrice = ''; final TextEditingController nameController = TextEditingController(); final TextEditingController priceController = TextEditingController(); await showDialog( context: context, builder: (context) => StatefulBuilder( builder: (context, setModalState) { void addVariant() { if (tempName.isNotEmpty && tempPrice.isNotEmpty) { final parsedPrice = int.tryParse(tempPrice); if (parsedPrice != null) { setModalState(() { variants[tempName] = parsedPrice; tempName = ''; tempPrice = ''; nameController.clear(); priceController.clear(); }); } } } return AlertDialog( title: const Text('価格設定', style: TextStyle(fontWeight: FontWeight.bold)), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ TextFormField( initialValue: manual?.toString() ?? '', keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: '販売価格 (税込)', hintText: '手動で設定する場合に入力', suffixText: '円', border: OutlineInputBorder(), ), style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18), onChanged: (v) => setModalState(() => manual = int.tryParse(v)), ), const SizedBox(height: 24), const Text('提供サイズ選択', style: TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 8), Wrap( spacing: 8.0, runSpacing: 4.0, children: [ for (var preset in ['グラス (90ml)', '一合 (180ml)', 'ボトル (720ml)']) ChoiceChip( label: Text(preset), selected: tempName == preset, onSelected: (selected) { setModalState(() { if (selected) { tempName = preset; nameController.text = preset; } }); }, backgroundColor: Theme.of(context).extension()!.surfaceSubtle, selectedColor: Theme.of(context).extension()!.brandAccent.withValues(alpha: 0.3), labelStyle: TextStyle( color: (tempName == preset) ? Theme.of(context).extension()!.brandPrimary : Theme.of(context).extension()!.textPrimary, fontWeight: (tempName == preset) ? FontWeight.bold : null, ), ), ], ), const SizedBox(height: 12), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 3, child: TextField( controller: nameController, decoration: const InputDecoration( labelText: '名称', hintText: '例: 徳利', isDense: true, border: OutlineInputBorder(), ), onChanged: (v) => tempName = v, ), ), const SizedBox(width: 8), Expanded( flex: 2, child: TextField( controller: priceController, keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: '価格', suffixText: '円', isDense: true, border: OutlineInputBorder(), ), onChanged: (v) => tempPrice = v, onSubmitted: (_) => addVariant(), ), ), ], ), const SizedBox(height: 8), SizedBox( width: double.infinity, child: ElevatedButton.icon( icon: const Icon(LucideIcons.plus), label: const Text('リストに追加'), style: ElevatedButton.styleFrom( backgroundColor: (tempName.isNotEmpty && tempPrice.isNotEmpty) ? Theme.of(context).extension()!.brandAccent : Theme.of(context).extension()!.surfaceSubtle, foregroundColor: Colors.white, ), onPressed: (tempName.isNotEmpty && tempPrice.isNotEmpty) ? addVariant : null, ), ), const SizedBox(height: 16), if (variants.isNotEmpty) Container( decoration: BoxDecoration( border: Border.all(color: Theme.of(context).extension()!.divider), borderRadius: BorderRadius.circular(8), ), child: Column( children: [ ...variants.entries.map((e) => Column( children: [ ListTile( dense: true, title: Text(e.key, style: const TextStyle(fontWeight: FontWeight.w500)), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ Text('${PricingCalculator.formatPrice(e.value)}円', style: const TextStyle(fontWeight: FontWeight.bold)), const SizedBox(width: 8), IconButton( icon: Icon(LucideIcons.x, color: Theme.of(context).extension()!.iconSubtle, size: 18), onPressed: () { setModalState(() { variants.remove(e.key); }); }, ), ], ), ), if (e.key != variants.keys.last) const Divider(height: 1), ], )), ], ), ), const SizedBox(height: 24), ExpansionTile( title: Text('原価・掛率設定 (自動計算)', style: TextStyle(fontSize: 14, color: Theme.of(context).extension()!.textSecondary)), children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 8), child: Column( children: [ TextFormField( initialValue: cost?.toString() ?? '', keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: '仕入れ値 (円)', suffixText: '円', border: OutlineInputBorder(), prefixIcon: Icon(Icons.currency_yen), ), onChanged: (v) => setModalState(() => cost = int.tryParse(v)), ), const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('掛率: ${markup.toStringAsFixed(1)}倍'), TextButton( child: const Text('リセット'), onPressed: () => setModalState(() => markup = argProfile.defaultMarkup), ) ], ), Slider( value: markup, min: 1.0, max: 5.0, divisions: 40, label: markup.toStringAsFixed(1), activeColor: Theme.of(context).extension()!.brandAccent, onChanged: (v) => setModalState(() => markup = v), ), Text( '参考計算価格: ${PricingCalculator.formatPrice(PricingCalculator.roundUpTo50((cost ?? 0) * markup))}円 (50円切上)', style: TextStyle(color: Theme.of(context).extension()!.textSecondary, fontSize: 12), ), ], ), ), ], ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('キャンセル'), ), ElevatedButton( onPressed: () { _updatePricing( costPrice: cost, manualPrice: manual, markup: markup, priceVariants: variants.isEmpty ? null : variants, ); Navigator.pop(context); }, child: const Text('保存'), ), ], ); } ), ); } Future _updatePricing({int? costPrice, int? manualPrice, double? markup, Map? priceVariants}) async { final box = Hive.box('sake_items'); final newItem = sake.copyWith( costPrice: costPrice, manualPrice: manualPrice, markup: markup ?? sake.userData.markup, priceVariants: priceVariants, isUserEdited: true, ); await box.put(sake.key, newItem); onUpdated(newItem); } }