import 'package:flutter/material.dart'; import 'package:lucide_icons/lucide_icons.dart'; import '../../models/sake_item.dart'; import '../../theme/app_colors.dart'; import '../sake_radar_chart.dart'; class SakeDetailChart extends StatelessWidget { final SakeItem sake; final ValueChanged>? onTasteStatsEdited; const SakeDetailChart({ super.key, required this.sake, this.onTasteStatsEdited, }); @override Widget build(BuildContext context) { if (sake.hiddenSpecs.tasteStats.isEmpty || sake.itemType == ItemType.set) { return const SizedBox.shrink(); } final appColors = Theme.of(context).extension()!; return Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Column( children: [ Row( children: [ Icon(LucideIcons.barChart2, size: 16, color: Theme.of(context).colorScheme.onSurface), const SizedBox(width: 8), Text( 'Visual Tasting', style: Theme.of(context).textTheme.labelLarge?.copyWith( color: Theme.of(context).colorScheme.onSurface, fontWeight: FontWeight.bold, ), ), const Spacer(), if (onTasteStatsEdited != null) IconButton( icon: Icon(LucideIcons.pencil, size: 18, color: appColors.iconSubtle), tooltip: 'チャートを手動編集', onPressed: () => _showEditModal(context), padding: EdgeInsets.zero, constraints: const BoxConstraints(minWidth: 36, minHeight: 36), ), ], ), const SizedBox(height: 16), SizedBox( height: 200, child: SakeRadarChart( tasteStats: { 'aroma': sake.hiddenSpecs.sakeTasteStats.aroma.round(), 'sweetness': sake.hiddenSpecs.sakeTasteStats.sweetness.round(), 'acidity': sake.hiddenSpecs.sakeTasteStats.acidity.round(), 'bitterness': sake.hiddenSpecs.sakeTasteStats.bitterness.round(), 'body': sake.hiddenSpecs.sakeTasteStats.body.round(), }, primaryColor: Theme.of(context).primaryColor, ), ), ], ), ); } void _showEditModal(BuildContext context) { final currentStats = { 'aroma': sake.hiddenSpecs.sakeTasteStats.aroma.round(), 'sweetness': sake.hiddenSpecs.sakeTasteStats.sweetness.round(), 'acidity': sake.hiddenSpecs.sakeTasteStats.acidity.round(), 'bitterness': sake.hiddenSpecs.sakeTasteStats.bitterness.round(), 'body': sake.hiddenSpecs.sakeTasteStats.body.round(), }; showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (context) => _TasteEditModal( initialStats: currentStats, onSave: (newStats) { onTasteStatsEdited?.call(newStats); }, ), ); } } /// 五味チャート編集モーダル class _TasteEditModal extends StatefulWidget { final Map initialStats; final ValueChanged> onSave; const _TasteEditModal({ required this.initialStats, required this.onSave, }); @override State<_TasteEditModal> createState() => _TasteEditModalState(); } class _TasteEditModalState extends State<_TasteEditModal> { late Map _editingStats; // 日本語ラベル static const Map _labels = { 'aroma': '香り', 'sweetness': '甘み', 'acidity': '酸味', 'bitterness': 'キレ', 'body': 'コク', }; // アイコン static const Map _icons = { 'aroma': LucideIcons.wind, 'sweetness': LucideIcons.candy, 'acidity': LucideIcons.citrus, 'bitterness': LucideIcons.zap, 'body': LucideIcons.droplets, }; @override void initState() { super.initState(); _editingStats = Map.from(widget.initialStats); } @override Widget build(BuildContext context) { final appColors = Theme.of(context).extension()!; return Container( decoration: BoxDecoration( color: Theme.of(context).scaffoldBackgroundColor, borderRadius: const BorderRadius.vertical(top: Radius.circular(20)), ), child: SafeArea( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Handle bar Container( width: 40, height: 4, margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration( color: appColors.divider, borderRadius: BorderRadius.circular(2), ), ), // Header Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'チャートを編集', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), TextButton( onPressed: () { // リセット: 元の値に戻す setState(() { _editingStats = Map.from(widget.initialStats); }); }, child: Text( 'リセット', style: TextStyle(color: appColors.textSecondary), ), ), ], ), const SizedBox(height: 8), Text( 'AI解析の値を手動で微調整できます', style: TextStyle( fontSize: 12, color: appColors.textSecondary, ), ), const SizedBox(height: 24), // Sliders ..._labels.entries.map((entry) { final key = entry.key; final label = entry.value; final icon = _icons[key] ?? LucideIcons.circle; final value = _editingStats[key] ?? 3; return Padding( padding: const EdgeInsets.only(bottom: 12), child: Row( children: [ Icon(icon, size: 18, color: appColors.brandPrimary), const SizedBox(width: 8), SizedBox( width: 40, child: Text( label, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: appColors.textPrimary, ), ), ), Expanded( child: SliderTheme( data: SliderTheme.of(context).copyWith( activeTrackColor: appColors.brandPrimary, inactiveTrackColor: appColors.divider, thumbColor: appColors.brandPrimary, overlayColor: appColors.brandPrimary.withValues(alpha: 0.1), trackHeight: 4, ), child: Slider( value: value.toDouble(), min: 0, max: 5, divisions: 5, onChanged: (newValue) { setState(() { _editingStats[key] = newValue.round(); }); }, ), ), ), SizedBox( width: 24, child: Text( '$value', textAlign: TextAlign.center, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: appColors.brandPrimary, ), ), ), ], ), ); }), const SizedBox(height: 16), // Buttons Row( children: [ Expanded( child: OutlinedButton( onPressed: () => Navigator.pop(context), style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 14), ), child: const Text('キャンセル'), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton( onPressed: () { widget.onSave(_editingStats); Navigator.pop(context); }, style: ElevatedButton.styleFrom( backgroundColor: appColors.brandPrimary, foregroundColor: appColors.surfaceSubtle, padding: const EdgeInsets.symmetric(vertical: 14), ), child: const Text('保存'), ), ), ], ), ], ), ), ), ); } }