import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; import 'dashboard_screen.dart'; import 'login_screen.dart'; // pastikan ada LoginScreen import 'riwayat_screen.dart'; // Sesuaikan baseUrl sesuai environment: // - Android emulator: 'http://10.0.2.2:8000' // - iOS simulator: 'http://localhost:8000' // - Perangkat nyata: 'http://:8000' // Untuk Web, gunakan host yang bisa diakses browser. const String baseUrl = 'http://localhost:8000'; class ProfileScreen extends StatefulWidget { final String token; const ProfileScreen({Key? key, required this.token}) : super(key: key); @override State createState() => _ProfileScreenState(); } class _ProfileScreenState extends State { late Future?> futureProfile; @override void initState() { super.initState(); futureProfile = fetchProfile(); } Future?> fetchProfile() async { final url = Uri.parse('$baseUrl/api/employee/profile'); try { final response = await http.get( url, headers: { 'Authorization': 'Bearer ${widget.token}', 'Accept': 'application/json', }, ); if (response.statusCode == 200) { final body = jsonDecode(response.body); return body['data'] as Map?; } else { return null; } } catch (_) { return null; } } void _refresh() { setState(() { futureProfile = fetchProfile(); }); } Future _logout() async { final uri = Uri.parse('$baseUrl/api/logout'); try { final resp = await http.post( uri, headers: { 'Authorization': 'Bearer ${widget.token}', 'Accept': 'application/json', }, ); if (resp.statusCode == 200) { final prefs = await SharedPreferences.getInstance(); await prefs.remove('token'); if (!mounted) return; Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (_) => const LoginScreen()), (route) => false, ); } else { String msg = 'Gagal logout'; try { final b = jsonDecode(resp.body); msg = b['message'] ?? msg; } catch (_) {} ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(msg))); } } catch (e) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('Error saat logout: $e'))); } } @override Widget build(BuildContext context) { return FutureBuilder?>( future: futureProfile, builder: (context, snapshot) { // Loading if (snapshot.connectionState == ConnectionState.waiting) { return Scaffold( backgroundColor: const Color(0xFFF7F9FB), appBar: AppBar( title: const Text('Profil Saya'), backgroundColor: const Color(0xFF4F8DFD), elevation: 0, centerTitle: true, ), body: const Center(child: CircularProgressIndicator()), ); } // Error atau null if (snapshot.hasError || snapshot.data == null) { return Scaffold( backgroundColor: const Color(0xFFF7F9FB), appBar: AppBar( title: const Text('Profil Saya'), backgroundColor: const Color(0xFF4F8DFD), elevation: 0, centerTitle: true, ), body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Text('Gagal memuat data profil'), const SizedBox(height: 8), ElevatedButton( onPressed: _refresh, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF4F8DFD), ), child: const Text('Coba Lagi'), ), ], ), ), ); } // Data tersedia final user = snapshot.data!; final profile = user['profile'] as Map? ?? {}; final name = (user['name'] ?? '-').toString(); final email = (user['email'] ?? '-').toString(); final phone = (profile['phone_number'] ?? '-').toString(); final nip = (profile['nip'] ?? '-').toString(); final position = (profile['position'] ?? '-').toString(); final photoPath = profile['profile_photo']; final photoUrl = photoPath != null ? '$baseUrl/storage/$photoPath' : null; return Scaffold( backgroundColor: const Color(0xFFF7F9FB), appBar: AppBar( title: const Text('Profil Saya'), backgroundColor: const Color(0xFF4F8DFD), elevation: 0, centerTitle: true, ), body: SingleChildScrollView( child: Column( children: [ // Header: avatar + nama (tanpa tombol upload) Container( width: double.infinity, decoration: const BoxDecoration( color: Color(0xFF4F8DFD), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24), ), ), child: Column( children: [ const SizedBox(height: 40), CircleAvatar( radius: 40, backgroundColor: Colors.white, child: CircleAvatar( radius: 36, backgroundImage: photoUrl != null ? NetworkImage(photoUrl) : const AssetImage('assets/logom.png') as ImageProvider, ), ), const SizedBox(height: 12), Text( name, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 20, color: Colors.white, ), ), const SizedBox(height: 24), ], ), ), const SizedBox(height: 18), // Informasi Pribadi tanpa edit Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0), child: 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.03), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Informasi Pribadi', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 15, ), ), const SizedBox(height: 12), _ProfileInfoRow(label: 'Nama', value: name), _ProfileInfoRow(label: 'Email', value: email), _ProfileInfoRow(label: 'No. Telepon', value: phone), _ProfileInfoRow(label: 'NIP', value: nip), _ProfileInfoRow(label: 'Jabatan', value: position), ], ), ), ), const SizedBox(height: 18), // Menu hanya logout Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0), child: Column( children: [ _ProfileMenuItem( icon: Icons.logout, label: 'Keluar', color: Colors.red, onTap: () async { final confirmed = await showDialog( context: context, builder: (_) => AlertDialog( title: const Text('Logout'), content: const Text( 'Yakin ingin logout?', ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Batal'), ), TextButton( onPressed: () => Navigator.pop(context, true), child: const Text('Ya'), ), ], ), ) ?? false; if (confirmed) { await _logout(); } }, ), ], ), ), const SizedBox(height: 24), ], ), ), bottomNavigationBar: BottomNavigationBar( currentIndex: 2, selectedItemColor: const Color(0xFF4F8DFD), unselectedItemColor: Colors.black38, items: const [ BottomNavigationBarItem( icon: Icon(Icons.home_rounded), label: 'Beranda', ), BottomNavigationBarItem( icon: Icon(Icons.history), label: 'Riwayat', ), BottomNavigationBarItem( icon: Icon(Icons.person), label: 'Profil', ), ], type: BottomNavigationBarType.fixed, onTap: (index) { if (index == 2) return; if (index == 0) { Navigator.pushAndRemoveUntil( context, MaterialPageRoute( builder: (context) => DashboardScreen( userName: name, token: widget.token, ), ), (route) => false, ); } if (index == 1) { Navigator.pushAndRemoveUntil( context, MaterialPageRoute( builder: (context) => RiwayatScreen(userName: name, token: widget.token), ), (route) => false, ); } }, ), ); }, ); } } class _ProfileInfoRow extends StatelessWidget { final String label; final String value; const _ProfileInfoRow({required this.label, required this.value, Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 120, child: Text( label, style: const TextStyle(color: Colors.black54, fontSize: 13), ), ), Expanded( child: Text( value, style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 13), ), ), ], ), ); } } class _ProfileMenuItem extends StatelessWidget { final IconData icon; final String label; final VoidCallback onTap; final Color? color; const _ProfileMenuItem({ required this.icon, required this.label, required this.onTap, this.color, Key? key, }) : super(key: key); @override Widget build(BuildContext context) { return InkWell( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8), decoration: const BoxDecoration( border: Border(bottom: BorderSide(color: Color(0xFFF0F0F0))), ), child: Row( children: [ Icon(icon, color: color ?? const Color(0xFF4F8DFD)), const SizedBox(width: 16), Expanded( child: Text( label, style: TextStyle( color: color ?? Colors.black87, fontWeight: FontWeight.w500, fontSize: 15, ), ), ), const Icon(Icons.chevron_right, color: Color(0xFFB0B0B0)), ], ), ), ); } }