import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:lucide_icons/lucide_icons.dart'; import 'package:phosphor_flutter/phosphor_flutter.dart'; import 'package:shimmer/shimmer.dart'; import '../app_design.dart'; import '../app_state.dart'; import '../models/mobile_child_model.dart'; import '../services/api_service.dart'; import '../utils/nutrition_display_utils.dart'; import 'add_child_screen.dart'; import 'account_info_screen.dart'; import 'auth_screen.dart'; import 'help_screen.dart'; import 'security_screen.dart'; class ProfileScreen extends StatefulWidget { const ProfileScreen({super.key}); @override State createState() => _ProfileScreenState(); } class _ProfileScreenState extends State { final _api = ApiService(); final _state = SgiziAppState.instance; late Future _future; Map? _profile; bool _reminderEnabled = true; TimeOfDay _reminderTime = const TimeOfDay(hour: 8, minute: 0); @override void initState() { super.initState(); _state.addListener(_onStateChanged); _future = _load(); } @override void dispose() { _state.removeListener(_onStateChanged); super.dispose(); } void _onStateChanged() { if (!mounted) return; setState(() {}); } Future _load() async { if (!_state.isAuthenticated) return; final results = await Future.wait([ _api.getChildren(), _api.getProfile(), ]); final children = results[0] as List; final profile = results[1] as Map; _state.setChildren(children); _profile = profile; } void _retry() => setState(() => _future = _load()); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF5F7F6), body: SafeArea( child: FutureBuilder( future: _future, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const _ProfileSkeleton(); } if (snapshot.hasError) { return _ProfileError(onRetry: _retry); } final name = (_profile?['name'] as String? ?? 'Siti Aminah').trim(); final phone = (_profile?['phone'] as String? ?? '-').trim(); final role = (_profile?['role'] as String? ?? 'orang_tua').trim(); return SingleChildScrollView( physics: const BouncingScrollPhysics(), padding: const EdgeInsets.fromLTRB(20, 14, 20, 28), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _ProfileHeaderCard( name: name.isEmpty ? 'Siti Aminah' : name, subtitle: _profileRoleToLabel(role, phone), onEditTap: () => Navigator.of(context).push( fadeRoute(const AccountInfoScreen()), ), ), const SizedBox(height: 18), _ChildrenSection( children: _state.children, activeChildId: _state.activeChildId, onAdd: () => Navigator.of(context).push( fadeRoute(const AddChildScreen()), ), onSelect: (id) => _state.setActiveChild(id), ), const SizedBox(height: 22), Text( 'Pengaturan', style: AppTypography.h2.copyWith(color: SgColors.textPrimary), ), const SizedBox(height: 12), _SettingsCard( reminderEnabled: _reminderEnabled, reminderTime: _reminderTime, onToggleReminder: (v) => setState(() => _reminderEnabled = v), onPickTime: () async { final picked = await showTimePicker( context: context, initialTime: _reminderTime, builder: (context, child) => Theme( data: Theme.of(context).copyWith( colorScheme: Theme.of(context).colorScheme.copyWith( primary: const Color(0xFF0B7A86), ), ), child: child!, ), ); if (picked == null) return; setState(() => _reminderTime = picked); }, onTapAccount: () => Navigator.of(context).push( fadeRoute(const AccountInfoScreen()), ), onTapPrivacy: () => Navigator.of(context).push( fadeRoute(const SecurityScreen()), ), onTapHelp: () => Navigator.of(context).push( fadeRoute(const HelpScreen()), ), ), const SizedBox(height: 18), _LogoutCard( onLogout: () async { final ok = await _confirmLogout(context); if (ok != true) return; _state.logout(); if (!context.mounted) return; Navigator.of(context).pushAndRemoveUntil( fadeRoute(const AuthScreen()), (_) => false, ); }, ), const SizedBox(height: 18), Text( 'S-GIZI • PREMIUM HEALTHCARE UI', textAlign: TextAlign.center, style: AppTypography.caption.copyWith( letterSpacing: 1, fontWeight: FontWeight.w800, ), ), ], ), ) .animate() .fadeIn(duration: 260.ms, curve: Curves.easeOut) .slideY(begin: 0.02, end: 0, duration: 260.ms, curve: Curves.easeOut); }, ), ), ); } } class _ProfileHeaderCard extends StatelessWidget { const _ProfileHeaderCard({ required this.name, required this.subtitle, required this.onEditTap, }); final String name; final String subtitle; final VoidCallback onEditTap; @override Widget build(BuildContext context) { return Container( width: double.infinity, decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFF085B63), Color(0xFF0B7A86)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(28), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.10), blurRadius: 26, offset: const Offset(0, 14), ), ], ), child: Stack( children: [ Positioned( right: -30, top: -40, child: Container( width: 140, height: 140, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.white.withValues(alpha: 0.08), ), ), ), Positioned( left: -50, bottom: -50, child: Container( width: 170, height: 170, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.white.withValues(alpha: 0.06), ), ), ), Padding( padding: const EdgeInsets.fromLTRB(18, 18, 18, 18), child: Row( children: [ Stack( clipBehavior: Clip.none, children: [ Container( width: 74, height: 74, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.white.withValues(alpha: 0.16), border: Border.all( color: Colors.white.withValues(alpha: 0.22), ), ), child: ClipOval( child: Padding( padding: const EdgeInsets.all(10), child: Image.asset( 'assets/image/onboarding_consultation.png', fit: BoxFit.cover, ), ), ), ) .animate(onPlay: (c) => c.repeat(reverse: true)) .moveY(begin: 0, end: -2, duration: 1800.ms), Positioned( right: 2, bottom: 2, child: Container( width: 12, height: 12, decoration: BoxDecoration( color: const Color(0xFF34C759), shape: BoxShape.circle, border: Border.all(color: Colors.white, width: 2), ), ), ), ], ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( name, style: AppTypography.h2.copyWith( color: Colors.white, fontSize: 20, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( subtitle, style: AppTypography.body.copyWith( color: Colors.white.withValues(alpha: 0.9), ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 10), Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.14), borderRadius: BorderRadius.circular(999), border: Border.all( color: Colors.white.withValues(alpha: 0.18), ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( PhosphorIconsRegular.sealCheck, color: Colors.white, size: 16, ), const SizedBox(width: 6), Text( 'Akun Terverifikasi', style: AppTypography.caption.copyWith( color: Colors.white, fontWeight: FontWeight.w700, ), ), ], ), ), ], ), ), const SizedBox(width: 10), _IconPillButton( icon: LucideIcons.pencil, onTap: onEditTap, ), ], ), ), ], ), ) .animate() .fadeIn(duration: 320.ms, curve: Curves.easeOut) .slideY(begin: -0.03, end: 0, duration: 320.ms, curve: Curves.easeOut); } } class _IconPillButton extends StatelessWidget { const _IconPillButton({required this.icon, required this.onTap}); final IconData icon; final VoidCallback onTap; @override Widget build(BuildContext context) { return Material( color: Colors.white.withValues(alpha: 0.14), borderRadius: BorderRadius.circular(999), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(999), child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(999), border: Border.all(color: Colors.white.withValues(alpha: 0.18)), ), child: Icon(icon, color: Colors.white, size: 18), ), ), ); } } class _ChildrenSection extends StatelessWidget { const _ChildrenSection({ required this.children, required this.activeChildId, required this.onAdd, required this.onSelect, }); final List children; final int? activeChildId; final VoidCallback onAdd; final ValueChanged onSelect; @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( 'Daftar Anak', style: AppTypography.h2.copyWith(color: SgColors.textPrimary), overflow: TextOverflow.ellipsis, ), ), TextButton.icon( onPressed: onAdd, icon: const Icon(LucideIcons.plus), label: const Text('Tambah'), style: TextButton.styleFrom( foregroundColor: const Color(0xFF0B7A86), ), ), ], ), const SizedBox(height: 10), if (children.isEmpty) HealthCard( child: Row( children: [ Container( width: 46, height: 46, decoration: BoxDecoration( color: const Color(0xFF7FD6C2).withValues(alpha: 0.18), borderRadius: BorderRadius.circular(16), ), child: const Icon( LucideIcons.baby, color: Color(0xFF0B7A86), ), ), const SizedBox(width: 12), const Expanded( child: Text( 'Belum ada data anak. Tambahkan anak untuk mulai memantau status gizi.', style: AppTypography.body, ), ), ], ), ) else SizedBox( height: 152, child: ListView.separated( scrollDirection: Axis.horizontal, physics: const BouncingScrollPhysics(), itemCount: children.length, separatorBuilder: (_, __) => const SizedBox(width: 12), itemBuilder: (context, index) { final c = children[index]; final active = c.id == activeChildId; return _ChildCard( child: c, active: active, onTap: () => onSelect(c.id), ); }, ), ), ], ) .animate() .fadeIn(duration: 320.ms, curve: Curves.easeOut) .slideY(begin: 0.02, end: 0, duration: 320.ms, curve: Curves.easeOut); } } class _ChildCard extends StatelessWidget { const _ChildCard({ required this.child, required this.active, required this.onTap, }); final MobileChildModel child; final bool active; final VoidCallback onTap; @override Widget build(BuildContext context) { final status = (child.latestStatus ?? '').trim().isEmpty ? 'Status: Belum ada' : 'Status: ${child.latestStatus}'; return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(22), child: AnimatedContainer( duration: const Duration(milliseconds: 220), curve: Curves.easeOut, width: 260, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(22), border: Border.all( color: active ? const Color(0xFF0B7A86) : const Color(0xFFE2E8E6), width: active ? 1.8 : 1, ), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: active ? 0.10 : 0.06), blurRadius: active ? 26 : 18, offset: const Offset(0, 12), ), ], ), child: Row( children: [ Stack( children: [ ChildAvatar( name: child.nama, gender: child.jenisKelamin, photoUrl: child.photoUrl, radius: 28, ), if (active) Positioned( right: 0, top: 0, child: Container( width: 14, height: 14, decoration: BoxDecoration( color: const Color(0xFF0B7A86), shape: BoxShape.circle, border: Border.all(color: Colors.white, width: 2), ), ), ), ], ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( child.nama, style: AppTypography.h3.copyWith(color: SgColors.textPrimary), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( '${child.jenisKelamin == 'P' ? 'Perempuan' : 'Laki-laki'} • ${formatAgeFromBirthDate(child.tanggalLahir)}', style: AppTypography.caption, maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 10), Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), decoration: BoxDecoration( color: active ? const Color(0xFF7FD6C2).withValues(alpha: 0.22) : const Color(0xFFF1F5F4), borderRadius: BorderRadius.circular(999), ), child: Text( status, style: AppTypography.caption.copyWith( color: active ? const Color(0xFF085B63) : SgColors.textSecondary, fontWeight: FontWeight.w700, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), ), const SizedBox(width: 6), PopupMenuButton( onSelected: (value) async { if (value == 'edit') { await _showEditChildDialog(context, child); } if (value == 'delete') { await _showDeleteChildDialog(context, child); } }, itemBuilder: (context) => const [ PopupMenuItem(value: 'edit', child: Text('Edit')), PopupMenuItem(value: 'delete', child: Text('Hapus')), ], icon: Icon( LucideIcons.moreVertical, color: active ? const Color(0xFF0B7A86) : SgColors.textSecondary, ), ), ], ), ), ).animate().scale( begin: const Offset(0.99, 0.99), end: const Offset(1, 1), duration: 220.ms, ); } } class _SettingsCard extends StatelessWidget { const _SettingsCard({ required this.reminderEnabled, required this.reminderTime, required this.onToggleReminder, required this.onPickTime, required this.onTapAccount, required this.onTapPrivacy, required this.onTapHelp, }); final bool reminderEnabled; final TimeOfDay reminderTime; final ValueChanged onToggleReminder; final VoidCallback onPickTime; final VoidCallback onTapAccount; final VoidCallback onTapPrivacy; final VoidCallback onTapHelp; @override Widget build(BuildContext context) { return HealthCard( padding: EdgeInsets.zero, child: Column( children: [ _SettingsTile( icon: LucideIcons.user, color: const Color(0xFF0B7A86), title: 'Informasi Akun', subtitle: 'Edit nama, foto, dan nomor telepon', onTap: onTapAccount, ), const Divider(height: 1, color: SgColors.border), Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), child: Row( children: [ _IconBadge( icon: LucideIcons.bell, color: const Color(0xFFFF7A1A), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Notifikasi & Pengingat', style: AppTypography.h3), const SizedBox(height: 2), Text( 'Reminder timbang, update data, dan nutrisi', style: AppTypography.caption, ), ], ), ), Switch( value: reminderEnabled, onChanged: onToggleReminder, activeColor: const Color(0xFF0B7A86), ), ], ), ), AnimatedCrossFade( duration: const Duration(milliseconds: 220), crossFadeState: reminderEnabled ? CrossFadeState.showFirst : CrossFadeState.showSecond, firstChild: Padding( padding: const EdgeInsets.fromLTRB(16, 0, 16, 14), child: InkWell( onTap: onPickTime, borderRadius: BorderRadius.circular(16), child: Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), decoration: BoxDecoration( color: const Color(0xFF7FD6C2).withValues(alpha: 0.16), borderRadius: BorderRadius.circular(16), border: Border.all( color: const Color(0xFF7FD6C2).withValues(alpha: 0.35), ), ), child: Row( children: [ const Icon( LucideIcons.clock3, color: Color(0xFF0B7A86), size: 18, ), const SizedBox(width: 10), Expanded( child: Text( 'Jam pengingat: ${_formatTime(reminderTime)}', style: AppTypography.body.copyWith( color: SgColors.textPrimary, ), overflow: TextOverflow.ellipsis, ), ), const Icon( LucideIcons.chevronRight, color: Color(0xFF0B7A86), ), ], ), ), ), ), secondChild: const SizedBox.shrink(), ), const Divider(height: 1, color: SgColors.border), _SettingsTile( icon: LucideIcons.shield, color: const Color(0xFF34A853), title: 'Privasi & Keamanan', subtitle: 'Kebijakan privasi dan keamanan akun', onTap: onTapPrivacy, ), const Divider(height: 1, color: SgColors.border), _SettingsTile( icon: LucideIcons.messageCircle, color: const Color(0xFF3B82F6), title: 'Bantuan', subtitle: 'FAQ, pusat bantuan, hubungi admin', onTap: onTapHelp, ), ], ), ) .animate() .fadeIn(duration: 280.ms, curve: Curves.easeOut) .slideY(begin: 0.02, end: 0, duration: 280.ms, curve: Curves.easeOut); } } class _SettingsTile extends StatelessWidget { const _SettingsTile({ required this.icon, required this.color, required this.title, required this.subtitle, required this.onTap, }); final IconData icon; final Color color; final String title; final String subtitle; final VoidCallback onTap; @override Widget build(BuildContext context) { return InkWell( onTap: onTap, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( children: [ _IconBadge(icon: icon, color: color), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: AppTypography.h3), const SizedBox(height: 2), Text( subtitle, style: AppTypography.caption, maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ), const Icon( LucideIcons.chevronRight, color: SgColors.textSecondary, ), ], ), ), ).animate().scale( begin: const Offset(1, 1), end: const Offset(0.99, 0.99), duration: 140.ms, ); } } class _IconBadge extends StatelessWidget { const _IconBadge({required this.icon, required this.color}); final IconData icon; final Color color; @override Widget build(BuildContext context) { return Container( width: 42, height: 42, decoration: BoxDecoration( color: color.withValues(alpha: 0.14), borderRadius: BorderRadius.circular(16), ), child: Icon(icon, color: color, size: 20), ); } } class _LogoutCard extends StatelessWidget { const _LogoutCard({required this.onLogout}); final VoidCallback onLogout; @override Widget build(BuildContext context) { return HealthCard( color: const Color(0xFFFFF5F5), borderColor: const Color(0xFFFFCDD2), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: const Color(0xFFE53935).withValues(alpha: 0.12), borderRadius: BorderRadius.circular(16), ), child: const Icon( LucideIcons.logOut, color: Color(0xFFE53935), ), ), const SizedBox(width: 12), const Expanded( child: Text( 'Keluar Aplikasi', style: AppTypography.h3, overflow: TextOverflow.ellipsis, ), ), TextButton( onPressed: onLogout, style: TextButton.styleFrom(foregroundColor: const Color(0xFFE53935)), child: const Text('Keluar'), ), ], ), ) .animate() .fadeIn(duration: 240.ms, curve: Curves.easeOut) .slideY(begin: 0.02, end: 0, duration: 240.ms, curve: Curves.easeOut); } } class _ProfileSkeleton extends StatelessWidget { const _ProfileSkeleton(); @override Widget build(BuildContext context) { return Shimmer.fromColors( baseColor: const Color(0xFFE8EEEC), highlightColor: const Color(0xFFF7FAF9), child: SingleChildScrollView( padding: const EdgeInsets.fromLTRB(20, 14, 20, 28), child: Column( children: [ Container( height: 124, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(28), ), ), const SizedBox(height: 18), Container( height: 168, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(22), ), ), const SizedBox(height: 22), Container( height: 260, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(22), ), ), const SizedBox(height: 18), Container( height: 74, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(22), ), ), ], ), ), ); } } class _ProfileError extends StatelessWidget { const _ProfileError({required this.onRetry}); final VoidCallback onRetry; @override Widget build(BuildContext context) { return ErrorState( message: 'Profil tidak dapat dimuat. Periksa koneksi lalu coba lagi.', onRetry: onRetry, ); } } Future _confirmLogout(BuildContext context) { return showGeneralDialog( context: context, barrierLabel: 'logout', barrierDismissible: true, barrierColor: Colors.black.withValues(alpha: 0.38), pageBuilder: (context, _, __) { return Center( child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), child: Padding( padding: const EdgeInsets.all(20), child: Material( color: Colors.transparent, child: Container( width: double.infinity, constraints: const BoxConstraints(maxWidth: 420), padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(22), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.12), blurRadius: 30, offset: const Offset(0, 18), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: const [ Icon( LucideIcons.logOut, color: Color(0xFFE53935), ), SizedBox(width: 10), Expanded( child: Text( 'Konfirmasi Keluar', style: AppTypography.h2, overflow: TextOverflow.ellipsis, ), ), ], ), const SizedBox(height: 10), const Text( 'Apakah Anda yakin ingin keluar?', style: AppTypography.body, ), const SizedBox(height: 16), Row( children: [ Expanded( child: SizedBox( height: 52, child: OutlinedButton( onPressed: () => Navigator.of(context).pop(false), style: OutlinedButton.styleFrom( foregroundColor: SgColors.textPrimary, side: const BorderSide(color: SgColors.border), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), ), child: const Text( 'Batal', style: TextStyle(fontWeight: FontWeight.w700), ), ), ), ), const SizedBox(width: 10), Expanded( child: SizedBox( height: 52, child: FilledButton( onPressed: () => Navigator.of(context).pop(true), style: FilledButton.styleFrom( backgroundColor: const Color(0xFFE53935), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), ), child: const Text( 'Keluar', style: TextStyle(fontWeight: FontWeight.w800), ), ), ), ), ], ), ], ), ).animate().fadeIn(duration: 180.ms).scale( begin: const Offset(0.98, 0.98), end: const Offset(1, 1), duration: 180.ms, curve: Curves.easeOut, ), ), ), ), ); }, transitionDuration: const Duration(milliseconds: 180), transitionBuilder: (context, animation, _, child) { return FadeTransition( opacity: CurvedAnimation(parent: animation, curve: Curves.easeOut), child: child, ); }, ); } String _formatTime(TimeOfDay time) { final h = time.hour.toString().padLeft(2, '0'); final m = time.minute.toString().padLeft(2, '0'); return '$h:$m'; } void _toast(BuildContext context, String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(message)), ); } String _profileRoleToLabel(String role, String fallbackPhone) { final v = role.toLowerCase(); if (v.contains('ortu') || v.contains('orang_tua') || v.contains('parent')) { return 'Orang Tua Aktif'; } if (v.contains('admin')) { return 'Admin'; } return fallbackPhone.isEmpty ? 'Akun Aktif' : fallbackPhone; } Future _showEditChildDialog(BuildContext context, MobileChildModel child) async { final api = ApiService(); final nameController = TextEditingController(text: child.nama); String gender = child.jenisKelamin; DateTime? birthDate = DateTime.tryParse(child.tanggalLahir); await showDialog( context: context, builder: (dialogContext) { return StatefulBuilder( builder: (context, setLocal) { return AlertDialog( title: const Text('Edit Data Anak'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: nameController, decoration: const InputDecoration(labelText: 'Nama Anak'), ), const SizedBox(height: 10), DropdownButtonFormField( value: gender, items: const [ DropdownMenuItem(value: 'L', child: Text('Laki-laki')), DropdownMenuItem(value: 'P', child: Text('Perempuan')), ], onChanged: (v) { if (v == null) return; setLocal(() => gender = v); }, decoration: const InputDecoration(labelText: 'Jenis Kelamin'), ), const SizedBox(height: 10), OutlinedButton( onPressed: () async { final picked = await showDatePicker( context: context, initialDate: birthDate ?? DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime.now(), ); if (picked != null) { setLocal(() => birthDate = picked); } }, child: Text( birthDate == null ? 'Pilih Tanggal Lahir' : 'Tanggal: ${birthDate!.toIso8601String().split('T').first}', ), ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.of(dialogContext).pop(), child: const Text('Batal'), ), FilledButton( onPressed: () async { try { await api.updateChild( childId: child.id, data: { 'nama': nameController.text.trim(), 'tanggal_lahir': (birthDate ?? DateTime.now()) .toIso8601String() .split('T') .first, 'jenis_kelamin': gender, }, ); final children = await api.getChildren(); SgiziAppState.instance.setChildren(children); if (dialogContext.mounted) Navigator.of(dialogContext).pop(); } catch (e) { if (!dialogContext.mounted) return; ScaffoldMessenger.of(dialogContext).showSnackBar( SnackBar(content: Text('Gagal edit anak: $e')), ); } }, child: const Text('Simpan'), ), ], ); }, ); }, ); } Future _showDeleteChildDialog(BuildContext context, MobileChildModel child) async { final api = ApiService(); final confirm = await showDialog( context: context, builder: (dialogContext) => AlertDialog( title: const Text('Hapus Anak'), content: Text('Yakin ingin menghapus data ${child.nama}?'), actions: [ TextButton( onPressed: () => Navigator.of(dialogContext).pop(false), child: const Text('Batal'), ), FilledButton( onPressed: () => Navigator.of(dialogContext).pop(true), style: FilledButton.styleFrom(backgroundColor: const Color(0xFFE53935)), child: const Text('Hapus'), ), ], ), ); if (confirm != true) return; try { await api.deleteChild(childId: child.id); final children = await api.getChildren(); SgiziAppState.instance.setChildren(children); } catch (e) { if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Gagal hapus anak: $e')), ); } }