import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/constants.dart'; import '../../core/time.dart'; import '../../data/db/daos/habit_dao.dart'; import '../../domain/models/habit.dart'; import '../../domain/rules/active_habit_quota.dart'; import '../../state/providers.dart'; class HabitCreateScreen extends ConsumerStatefulWidget { const HabitCreateScreen({super.key}); @override ConsumerState createState() => _HabitCreateScreenState(); } class _HabitCreateScreenState extends ConsumerState { final _formKey = GlobalKey(); final _titleCtrl = TextEditingController(); final _framedCtrl = TextEditingController(); HabitType _type = HabitType.build; FrameLevel _level = FrameLevel.l2; bool _saving = false; @override void dispose() { _titleCtrl.dispose(); _framedCtrl.dispose(); super.dispose(); } Future _save() async { if (!_formKey.currentState!.validate()) return; setState(() => _saving = true); try { final dao = ref.read(habitDaoProvider); final count = await dao.countActive( userId: kLocalDefaultUserId, type: _type, ); final quota = judgeActiveHabitQuota(type: _type, currentActiveCount: count); if (!quota.allowed) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(quota.reason)), ); return; } await dao.insertWithVariants(HabitDraft( userId: kLocalDefaultUserId, type: _type, title: _titleCtrl.text.trim(), // Placeholder: vertical-slice uses the first seeded protocol. protocolId: _type == HabitType.build ? 'morning_sunlight' : null, breakProtocolId: _type == HabitType.breakHabit ? 'alcohol' : null, frameLevel: _level, frameFramedText: _framedCtrl.text.trim(), startedAt: _ymd(nowKst()), )); if (!mounted) return; Navigator.of(context).pop(); } catch (e) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('저장 실패: $e')), ); } finally { if (mounted) setState(() => _saving = false); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('새 습관')), body: Padding( padding: const EdgeInsets.all(16), child: Form( key: _formKey, child: ListView( children: [ TextFormField( controller: _titleCtrl, decoration: const InputDecoration(labelText: '제목'), validator: (v) => (v == null || v.trim().isEmpty) ? '제목을 입력하세요' : null, ), const SizedBox(height: 16), DropdownButtonFormField( initialValue: _type, items: const [ DropdownMenuItem(value: HabitType.build, child: Text('만들기 (build)')), DropdownMenuItem( value: HabitType.breakHabit, child: Text('없애기 (break)')), ], onChanged: (v) => setState(() => _type = v ?? HabitType.build), decoration: const InputDecoration(labelText: '타입'), ), const SizedBox(height: 16), DropdownButtonFormField( initialValue: _level, items: const [ DropdownMenuItem(value: FrameLevel.l2, child: Text('L2 · 조건부 긍정')), DropdownMenuItem(value: FrameLevel.l3, child: Text('L3 · 정체성')), ], onChanged: (v) => setState(() => _level = v ?? FrameLevel.l2), decoration: const InputDecoration(labelText: '프레임 레벨'), ), const SizedBox(height: 16), TextFormField( controller: _framedCtrl, decoration: const InputDecoration( labelText: '프레임 문구', hintText: '예: 아침 햇빛을 10분 받는다', ), maxLines: 2, validator: (v) => (v == null || v.trim().isEmpty) ? '프레임 문구를 입력하세요' : null, ), const SizedBox(height: 24), FilledButton( onPressed: _saving ? null : _save, child: Text(_saving ? '저장 중...' : '저장'), ), ], ), ), ), ); } } String _ymd(DateTime d) => '${d.year.toString().padLeft(4, '0')}-' '${d.month.toString().padLeft(2, '0')}-' '${d.day.toString().padLeft(2, '0')}';