import 'package:flutter/material.dart'; import '../../services/checkin_dataset.dart'; import '../../services/firebase_service.dart'; import '../../services/daily_checkin_service.dart'; // ─── Design tokens ─────────────────────────────────────────────────────────── const Color _brown = Color(0xFF4A3C3C); const Color _lightBg = Color(0xFFFAF6F1); const Color _accent = Color(0xFF8B5E3C); const Color _grey = Color(0xFF9E9E9E); const Color _border = Color(0xFFE8E8E8); // ───────────────────────────────────────────────────────────────────────────── class DailyCheckinScreen extends StatefulWidget { final bool isMandatory; const DailyCheckinScreen({super.key, this.isMandatory = false}); @override State createState() => _DailyCheckinScreenState(); } class _DailyCheckinScreenState extends State { final PageController _pageController = PageController(); int _currentPage = 0; bool _isSaving = false; // Page 1 — dynamic late final CheckinEntry _moodEntry; late final CheckinEntry _cuacaEntry; late final CheckinEntry _suasanaEntry; // All answers final Map _answers = {}; // Budget slider state double _budget = 30000; bool _isLoadingData = false; @override void initState() { super.initState(); final daily = CheckinDataset.getDailyEntries(); _moodEntry = daily.mood; _cuacaEntry = daily.cuaca; _suasanaEntry = daily.suasana; _loadPreviousData(); } Future _loadPreviousData() async { setState(() => _isLoadingData = true); try { final user = FirebaseService().currentUser; if (user != null) { final userData = await FirebaseService().getUserData(user.uid); if (userData != null) { setState(() { // Sinkronkan default budget dengan Preferensi Diri _budget = userData.preferences.budgetRange.toDouble(); _answers['budget'] = _budget.toInt(); if (userData.dailyPreferences != null) { userData.dailyPreferences!.forEach((key, value) { if (key == 'budget') { _budget = (value as num).toDouble(); _answers['budget'] = value; } else { _answers[key] = value.toString(); } }); } }); } } } catch (e) { debugPrint('Error loading previous preferences: $e'); } finally { if (mounted) setState(() => _isLoadingData = false); } } @override void dispose() { _pageController.dispose(); super.dispose(); } // ── Helpers ──────────────────────────────────────────────────────────────── bool get _page1Complete => _answers.containsKey('mood') && _answers.containsKey('cuaca') && _answers.containsKey('suasana'); bool get _page2Complete => _answers.containsKey('keinginan') && _answers.containsKey('budget'); bool get _page3Complete => _answers.containsKey('waktu'); void _goNext() { if (_currentPage == 0 && !_page1Complete) { _showValidation('Mohon jawab semua pertanyaan di halaman ini.'); return; } if (_currentPage == 1 && !_page2Complete) { _showValidation('Mohon pilih keinginan dan budget-mu.'); return; } if (_currentPage < 2) { _pageController.nextPage( duration: const Duration(milliseconds: 350), curve: Curves.easeInOut, ); } } void _goPrev() { if (_currentPage > 0) { _pageController.previousPage( duration: const Duration(milliseconds: 350), curve: Curves.easeInOut, ); } } void _showValidation(String msg) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(msg, style: const TextStyle(fontFamily: 'Poppins')), backgroundColor: _accent, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), ); } Future _submit() async { if (!_page3Complete) { _showValidation('Pilih waktu kunjunganmu dulu ya.'); return; } setState(() => _isSaving = true); try { final user = FirebaseService().currentUser; if (user == null) throw Exception('User not logged in'); // Simpan budget sebagai int _answers['budget'] = _budget.toInt(); // Simpan ke Daily Preferences await DailyCheckinService().saveDailyPreferences(user.uid, _answers); // Sinkronkan juga ke Preferensi Diri (Master Profile) agar tetap sama final userData = await FirebaseService().getUserData(user.uid); if (userData != null) { final updatedPrefs = userData.preferences.copyWith( budgetRange: _budget.toInt(), ); await FirebaseService().updateUserPreferences(user.uid, updatedPrefs); } if (mounted) Navigator.of(context).pop(true); } catch (e) { debugPrint('Error saving check-in: $e'); if (mounted) _showValidation('Gagal menyimpan: $e'); } finally { if (mounted) setState(() => _isSaving = false); } } // ── Build ────────────────────────────────────────────────────────────────── @override Widget build(BuildContext context) { return Scaffold( backgroundColor: _lightBg, body: SafeArea( child: Column( children: [ _buildHeader(), _buildProgressDots(), Expanded( child: _isLoadingData ? const Center(child: CircularProgressIndicator(color: _accent)) : PageView( controller: _pageController, physics: const NeverScrollableScrollPhysics(), onPageChanged: (i) => setState(() => _currentPage = i), children: [_buildPage1(), _buildPage2(), _buildPage3()], ), ), _buildNavBar(), ], ), ), ); } // ── Header ───────────────────────────────────────────────────────────────── Widget _buildHeader() { final titles = [ 'Gimana Hari Ini? 🌤️', 'Kamu Lagi Mau Apa? 🍽️', 'Kapan Datangnya? 🕐', ]; final subs = [ 'Ceritakan mood, cuaca & suasanamu', 'Pilih keinginan & budget-mu', 'Waktu kunjungan membantu rekomendasi', ]; return Padding( padding: const EdgeInsets.fromLTRB(20, 16, 20, 0), child: Row( children: [ if (widget.isMandatory || _currentPage > 0) GestureDetector( onTap: _currentPage == 0 ? () => Navigator.of(context).pop() : _goPrev, child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all(color: _border), ), child: const Icon( Icons.arrow_back_ios_new, size: 16, color: _brown, ), ), ) else const SizedBox(width: 36), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: Text( titles[_currentPage], key: ValueKey(_currentPage), style: const TextStyle( fontFamily: 'Poppins', fontSize: 18, fontWeight: FontWeight.w700, color: _brown, ), ), ), AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: Text( subs[_currentPage], key: ValueKey('s$_currentPage'), style: const TextStyle( fontFamily: 'Poppins', fontSize: 12, color: _grey, ), ), ), ], ), ), if (!widget.isMandatory && _currentPage == 0) TextButton( onPressed: () => Navigator.of(context).pop(false), child: const Text( 'Lewati', style: TextStyle( fontFamily: 'Poppins', fontSize: 13, color: _grey, ), ), ), ], ), ); } // ── Progress dots ────────────────────────────────────────────────────────── Widget _buildProgressDots() { return Padding( padding: const EdgeInsets.symmetric(vertical: 14), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(3, (i) { final active = i == _currentPage; final done = i < _currentPage; return AnimatedContainer( duration: const Duration(milliseconds: 300), margin: const EdgeInsets.symmetric(horizontal: 4), width: active ? 24 : 8, height: 8, decoration: BoxDecoration( color: done ? _accent.withOpacity(0.4) : active ? _accent : _border, borderRadius: BorderRadius.circular(4), ), ); }), ), ); } // ── Page 1: Mood + Cuaca + Suasana ──────────────────────────────────────── Widget _buildPage1() { return SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), child: Column( children: [ _buildDynamicCard( icon: '😊', label: 'Mood', entry: _moodEntry, answerKey: 'mood', valueList: CheckinDataset.moodValues, ), const SizedBox(height: 16), _buildDynamicCard( icon: '🌤️', label: 'Cuaca', entry: _cuacaEntry, answerKey: 'cuaca', valueList: CheckinDataset.cuacaValues, ), const SizedBox(height: 16), _buildDynamicCard( icon: '👥', label: 'Suasana', entry: _suasanaEntry, answerKey: 'suasana', valueList: CheckinDataset.suasanaValues, ), const SizedBox(height: 20), ], ), ); } Widget _buildDynamicCard({ required String icon, required String label, required CheckinEntry entry, required String answerKey, required List valueList, }) { final selectedValue = _answers[answerKey]; return _card( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text(icon, style: const TextStyle(fontSize: 20)), const SizedBox(width: 8), Text( label, style: const TextStyle( fontFamily: 'Poppins', fontSize: 11, fontWeight: FontWeight.w600, color: _accent, letterSpacing: 0.5, ), ), ], ), const SizedBox(height: 8), Text( entry.question, style: const TextStyle( fontFamily: 'Poppins', fontSize: 14, fontWeight: FontWeight.w600, color: _brown, height: 1.4, ), ), const SizedBox(height: 12), Wrap( spacing: 8, runSpacing: 8, children: List.generate(entry.options.length, (i) { final val = valueList[i]; final selected = selectedValue == val; return GestureDetector( onTap: () => setState(() => _answers[answerKey] = val), child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8, ), decoration: BoxDecoration( color: selected ? _accent.withOpacity(0.12) : Colors.white, borderRadius: BorderRadius.circular(20), border: Border.all( color: selected ? _accent : _border, width: selected ? 1.5 : 1, ), ), child: Text( entry.options[i], style: TextStyle( fontFamily: 'Poppins', fontSize: 12, fontWeight: selected ? FontWeight.w600 : FontWeight.w400, color: selected ? _accent : _grey, ), ), ), ); }), ), ], ), ); } // ── Page 2: Keinginan + Budget ───────────────────────────────────────────── Widget _buildPage2() { final keinginanOptions = [ ('🥤', 'Minuman', 'minuman'), ('🍽️', 'Makanan', 'makanan'), ('☕🍽️', 'Keduanya', 'keduanya'), ]; return SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), child: Column( children: [ // Keinginan card _card( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Text('🛒', style: TextStyle(fontSize: 20)), SizedBox(width: 8), Text( 'KEINGINAN', style: TextStyle( fontFamily: 'Poppins', fontSize: 11, fontWeight: FontWeight.w600, color: _accent, letterSpacing: 0.5, ), ), ], ), const SizedBox(height: 8), const Text( 'Hari ini lagi mau apa?', style: TextStyle( fontFamily: 'Poppins', fontSize: 14, fontWeight: FontWeight.w600, color: _brown, ), ), const SizedBox(height: 14), Row( children: keinginanOptions.map((opt) { final (emoji, label, val) = opt; final selected = _answers['keinginan'] == val; return Expanded( child: GestureDetector( onTap: () => setState(() => _answers['keinginan'] = val), child: AnimatedContainer( duration: const Duration(milliseconds: 200), margin: const EdgeInsets.symmetric(horizontal: 4), padding: const EdgeInsets.symmetric(vertical: 14), decoration: BoxDecoration( color: selected ? _accent.withOpacity(0.12) : Colors.white, borderRadius: BorderRadius.circular(14), border: Border.all( color: selected ? _accent : _border, width: selected ? 1.5 : 1, ), ), child: Column( children: [ Text(emoji, style: const TextStyle(fontSize: 24)), const SizedBox(height: 4), Text( label, style: TextStyle( fontFamily: 'Poppins', fontSize: 12, fontWeight: selected ? FontWeight.w700 : FontWeight.w400, color: selected ? _accent : _grey, ), ), ], ), ), ), ); }).toList(), ), ], ), ), const SizedBox(height: 16), // Budget card _card( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Text('💰', style: TextStyle(fontSize: 20)), SizedBox(width: 8), Text( 'BUDGET', style: TextStyle( fontFamily: 'Poppins', fontSize: 11, fontWeight: FontWeight.w600, color: _accent, letterSpacing: 0.5, ), ), ], ), const SizedBox(height: 8), const Text( 'Sekitar berapa budget-mu?', style: TextStyle( fontFamily: 'Poppins', fontSize: 14, fontWeight: FontWeight.w600, color: _brown, ), ), const SizedBox(height: 16), Center( child: Container( padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 8, ), decoration: BoxDecoration( color: _accent.withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: Text( _formatRp(_budget.toInt()), style: const TextStyle( fontFamily: 'Poppins', fontSize: 20, fontWeight: FontWeight.w700, color: _accent, ), ), ), ), const SizedBox(height: 8), SliderTheme( data: SliderTheme.of(context).copyWith( activeTrackColor: _accent, inactiveTrackColor: _border, thumbColor: _accent, overlayColor: _accent.withOpacity(0.1), trackHeight: 4, ), child: Slider( value: _budget, min: 10000, max: 200000, divisions: 38, onChanged: (v) => setState(() { _budget = v; _answers['budget'] = v.toInt(); }), ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Rp 10K', style: const TextStyle( fontFamily: 'Poppins', fontSize: 11, color: _grey, ), ), Text( 'Rp 200K', style: const TextStyle( fontFamily: 'Poppins', fontSize: 11, color: _grey, ), ), ], ), ), ], ), ), const SizedBox(height: 20), ], ), ); } // ── Page 3: Waktu ────────────────────────────────────────────────────────── Widget _buildPage3() { final options = [ ('🌅', 'Pagi', 'pagi', '06.00 – 11.00'), ('☀️', 'Siang', 'siang', '11.00 – 15.00'), ('🌤️', 'Sore', 'sore', '15.00 – 18.00'), ('🌙', 'Malam', 'malam', '18.00 – 22.00'), ]; return SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), child: _card( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Text('🕐', style: TextStyle(fontSize: 20)), SizedBox(width: 8), Text( 'WAKTU KUNJUNGAN', style: TextStyle( fontFamily: 'Poppins', fontSize: 11, fontWeight: FontWeight.w600, color: _accent, letterSpacing: 0.5, ), ), ], ), const SizedBox(height: 8), const Text( 'Kapan kamu ke sini?', style: TextStyle( fontFamily: 'Poppins', fontSize: 14, fontWeight: FontWeight.w600, color: _brown, ), ), const SizedBox(height: 16), GridView.count( crossAxisCount: 2, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisSpacing: 12, mainAxisSpacing: 12, childAspectRatio: 1.1, children: options.map((opt) { final (emoji, label, val, time) = opt; final selected = _answers['waktu'] == val; return GestureDetector( onTap: () => setState(() => _answers['waktu'] = val), child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: selected ? _accent.withOpacity(0.12) : Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all( color: selected ? _accent : _border, width: selected ? 1.5 : 1, ), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(emoji, style: const TextStyle(fontSize: 26)), const SizedBox(height: 4), Text( label, style: TextStyle( fontFamily: 'Poppins', fontSize: 13, fontWeight: selected ? FontWeight.w700 : FontWeight.w500, color: selected ? _accent : _brown, ), ), Text( time, style: const TextStyle( fontFamily: 'Poppins', fontSize: 10, color: _grey, ), ), ], ), ), ); }).toList(), ), const SizedBox(height: 8), ], ), ), ); } // ── Bottom navigation bar ────────────────────────────────────────────────── Widget _buildNavBar() { final isLast = _currentPage == 2; return Container( padding: const EdgeInsets.fromLTRB(20, 12, 20, 16), decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, -2), ), ], ), child: Row( children: [ // Step indicator Text( '${_currentPage + 1} / 3', style: const TextStyle( fontFamily: 'Poppins', fontSize: 13, color: _grey, fontWeight: FontWeight.w500, ), ), const Spacer(), // Next / Submit button GestureDetector( onTap: _isSaving ? null : (isLast ? _submit : _goNext), child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 14), decoration: BoxDecoration( color: _accent, borderRadius: BorderRadius.circular(14), boxShadow: [ BoxShadow( color: _accent.withOpacity(0.3), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: _isSaving ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : Row( mainAxisSize: MainAxisSize.min, children: [ Text( isLast ? 'Selesai' : 'Lanjut', style: const TextStyle( fontFamily: 'Poppins', fontSize: 14, fontWeight: FontWeight.w600, color: Colors.white, ), ), const SizedBox(width: 6), Icon( isLast ? Icons.check_rounded : Icons.arrow_forward_rounded, color: Colors.white, size: 18, ), ], ), ), ), ], ), ); } // ── Helpers ──────────────────────────────────────────────────────────────── Widget _card({required Widget child}) { return Container( width: double.infinity, padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(18), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.04), blurRadius: 12, offset: const Offset(0, 4), ), ], ), child: child, ); } String _formatRp(int value) { final s = value.toString(); final buf = StringBuffer('Rp '); for (var i = 0; i < s.length; i++) { if (i > 0 && (s.length - i) % 3 == 0) buf.write('.'); buf.write(s[i]); } return buf.toString(); } }