Files
penyaluran_app/lib/app/modules/profile/views/profile_view.dart

591 lines
19 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:penyaluran_app/app/modules/profile/controllers/profile_controller.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
import 'dart:io';
class ProfileView extends GetView<ProfileController> {
const ProfileView({super.key});
// Helper untuk mengkonversi nilai role ke tampilan yang lebih baik
String _formatRoleName(String? role) {
if (role == null) return 'Pengguna';
switch (role.toLowerCase()) {
case 'warga':
return 'Warga';
case 'petugas_desa':
return 'Petugas Desa';
case 'admin_desa':
return 'Admin Desa';
case 'donatur':
return 'Donatur';
case 'admin':
return 'Administrator';
default:
// Kapitalisasi setiap kata dan ganti underscore dengan spasi
return role
.split('_')
.map((word) => word.isEmpty
? ''
: '${word[0].toUpperCase()}${word.substring(1).toLowerCase()}')
.join(' ');
}
}
// Helper untuk mendapatkan warna berdasarkan role
Color _getRoleColor(String? role) {
if (role == null) return AppTheme.primaryColor;
switch (role.toLowerCase()) {
case 'warga':
return AppTheme.infoColor;
case 'petugas_desa':
return AppTheme.primaryColor;
case 'donatur':
return AppTheme.successColor;
default:
return AppTheme.primaryColor;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Profil'),
actions: [
Obx(() {
if (controller.isEditing.value) {
return IconButton(
icon: const Icon(Icons.save),
onPressed: controller.updateProfile,
);
} else {
return IconButton(
icon: const Icon(Icons.edit),
onPressed: controller.toggleEditMode,
);
}
}),
],
),
body: Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
return SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildProfileHeader(),
const SizedBox(height: 24),
_buildProfileForm(),
const SizedBox(height: 24),
_buildPasswordSection(context),
],
),
);
}),
);
}
Widget _buildProfileHeader() {
return Center(
child: Column(
children: [
Obx(() {
// Jika user sedang dalam mode edit dan sudah memilih foto baru
if (controller.isEditing.value &&
controller.fotoProfilPath.isNotEmpty) {
return _buildProfileImage(
isLocalFile: true,
imagePath: controller.fotoProfilPath.value,
);
}
// Jika user sudah memiliki foto profil
else if (controller.fotoProfil.isNotEmpty) {
return _buildProfileImage(
isLocalFile: false,
imagePath: controller.fotoProfil.value,
);
}
// Default jika tidak ada foto
else {
return _buildDefaultProfileImage();
}
}),
// Tombol edit foto (hanya muncul dalam mode edit)
Obx(() {
if (controller.isEditing.value) {
return Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Tombol ambil dari kamera
_buildPhotoActionButton(
icon: Icons.camera_alt,
label: 'Kamera',
onPressed: () => controller.pickFotoProfilFromCamera(),
),
const SizedBox(width: 8),
// Tombol ambil dari galeri
_buildPhotoActionButton(
icon: Icons.photo_library,
label: 'Galeri',
onPressed: () => controller.pickFotoProfilFromGallery(),
),
// Tombol hapus foto (hanya jika ada foto yang dipilih)
if (controller.fotoProfilPath.isNotEmpty ||
controller.fotoProfil.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: _buildPhotoActionButton(
icon: Icons.delete,
label: 'Hapus',
onPressed: () => controller.clearFotoProfil(),
color: Colors.red,
),
),
],
),
);
} else {
return const SizedBox.shrink();
}
}),
const SizedBox(height: 16),
Obx(() => Text(
controller.user.value?.name ?? 'Pengguna',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
)),
const SizedBox(height: 4),
Obx(() {
final role = controller.user.value?.role;
final roleColor = _getRoleColor(role);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: roleColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: roleColor.withOpacity(0.3)),
),
child: Text(
_formatRoleName(role),
style: TextStyle(
fontSize: 14,
color: roleColor,
fontWeight: FontWeight.w500,
),
),
);
}),
],
),
);
}
// Widget foto profil default
Widget _buildDefaultProfileImage() {
return CircleAvatar(
radius: 60,
backgroundColor: AppTheme.primaryColor.withOpacity(0.1),
child: const Icon(
Icons.person,
size: 70,
color: AppTheme.primaryColor,
),
);
}
// Widget foto profil dengan gambar
Widget _buildProfileImage(
{required bool isLocalFile, required String imagePath}) {
return Stack(
children: [
// Widget untuk menampilkan foto profil
Container(
width: 120,
height: 120,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: AppTheme.primaryColor.withOpacity(0.5),
width: 3,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(60),
child: isLocalFile
? Image.file(
File(imagePath),
fit: BoxFit.cover,
width: 120,
height: 120,
errorBuilder: (context, error, stackTrace) =>
_buildDefaultProfileImage(),
)
: Image.network(
imagePath,
fit: BoxFit.cover,
width: 120,
height: 120,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
errorBuilder: (context, error, stackTrace) =>
_buildDefaultProfileImage(),
),
),
),
// Indikator loading saat mengupload
if (controller.isUploadingFoto.value)
Positioned.fill(
child: Container(
decoration: BoxDecoration(
color: Colors.black38,
shape: BoxShape.circle,
),
child: Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
),
),
],
);
}
// Widget tombol aksi foto
Widget _buildPhotoActionButton({
required IconData icon,
required String label,
required VoidCallback onPressed,
Color? color,
}) {
final buttonColor = color ?? AppTheme.primaryColor;
return InkWell(
onTap: onPressed,
borderRadius: BorderRadius.circular(8),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: buttonColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: buttonColor.withOpacity(0.3)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 16, color: buttonColor),
const SizedBox(width: 4),
Text(
label,
style: TextStyle(
fontSize: 12,
color: buttonColor,
fontWeight: FontWeight.w500,
),
),
],
),
),
);
}
Widget _buildProfileForm() {
return Obx(() {
final isEditing = controller.isEditing.value;
final user = controller.user.value;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Informasi Pribadi',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
// Nama
TextField(
controller: controller.nameController,
decoration: InputDecoration(
labelText: 'Nama Lengkap',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person),
enabled: isEditing,
),
),
const SizedBox(height: 16),
// Email
TextField(
controller: controller.emailController,
decoration: InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.email),
enabled: false, // Email tidak bisa diubah
),
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 16),
// Nomor Telepon
TextField(
controller: controller.phoneController,
decoration: InputDecoration(
labelText: 'Nomor Telepon',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.phone),
enabled: isEditing,
),
keyboardType: TextInputType.phone,
),
const SizedBox(height: 16),
// Informasi tambahan sesuai role
if (user != null) ...[
if (user.role?.toLowerCase() == 'warga') ...[
const SizedBox(height: 24),
const Text(
'Informasi Warga',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
Obx(() {
final roleData = controller.roleData.value;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildInfoRow(
Icons.perm_identity, 'NIK', roleData?['nik'] ?? '-'),
const SizedBox(height: 8),
_buildInfoRow(Icons.wc, 'Jenis Kelamin',
roleData?['jenis_kelamin'] ?? '-'),
const SizedBox(height: 8),
_buildInfoRow(
Icons.home, 'Alamat', roleData?['alamat'] ?? '-'),
if (user.desa != null) ...[
const SizedBox(height: 8),
_buildInfoRow(
Icons.location_city, 'Desa', user.desa!.nama),
const SizedBox(height: 8),
_buildInfoRow(Icons.location_on, 'Kecamatan',
user.desa!.kecamatan ?? ''),
const SizedBox(height: 8),
_buildInfoRow(Icons.location_on, 'Kabupaten',
user.desa!.kabupaten ?? ''),
const SizedBox(height: 8),
_buildInfoRow(Icons.location_on, 'Provinsi',
user.desa!.provinsi ?? ''),
],
],
);
}),
],
if (user.role?.toLowerCase() == 'donatur') ...[
const SizedBox(height: 24),
const Text(
'Informasi Donatur',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
Obx(() {
final roleData = controller.roleData.value;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildInfoRow(Icons.business, 'Instansi',
roleData?['instansi'] ?? '-'),
const SizedBox(height: 8),
_buildInfoRow(
Icons.work, 'Jabatan', roleData?['jabatan'] ?? '-'),
],
);
}),
],
if (user.role?.toLowerCase() == 'petugas_desa') ...[
const SizedBox(height: 24),
const Text(
'Informasi Petugas Desa',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
Obx(() {
final roleData = controller.roleData.value;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildInfoRow(Icons.badge, 'NIP', roleData?['nip'] ?? '-'),
if (user.desa != null) ...[
const SizedBox(height: 8),
_buildInfoRow(
Icons.location_city, 'Desa', user.desa!.nama),
const SizedBox(height: 8),
_buildInfoRow(Icons.location_on, 'Kecamatan',
user.desa!.kecamatan ?? ''),
const SizedBox(height: 8),
_buildInfoRow(Icons.location_on, 'Kabupaten',
user.desa!.kabupaten ?? ''),
const SizedBox(height: 8),
_buildInfoRow(Icons.location_on, 'Provinsi',
user.desa!.provinsi ?? ''),
],
],
);
}),
],
],
],
);
});
}
Widget _buildPasswordSection(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Keamanan',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
ElevatedButton.icon(
onPressed: () => _showChangePasswordDialog(context),
icon: const Icon(Icons.lock),
label: const Text('Ubah Password'),
style: ElevatedButton.styleFrom(
backgroundColor: AppTheme.primaryColor,
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 50),
),
),
],
);
}
void _showChangePasswordDialog(BuildContext context) {
final currentPasswordController = TextEditingController();
final newPasswordController = TextEditingController();
final confirmPasswordController = TextEditingController();
Get.dialog(
AlertDialog(
title: const Text('Ubah Password'),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: currentPasswordController,
decoration: const InputDecoration(
labelText: 'Password Saat Ini',
border: OutlineInputBorder(),
),
obscureText: true,
),
const SizedBox(height: 16),
TextField(
controller: newPasswordController,
decoration: const InputDecoration(
labelText: 'Password Baru',
border: OutlineInputBorder(),
),
obscureText: true,
),
const SizedBox(height: 16),
TextField(
controller: confirmPasswordController,
decoration: const InputDecoration(
labelText: 'Konfirmasi Password Baru',
border: OutlineInputBorder(),
),
obscureText: true,
),
],
),
),
actions: [
TextButton(
onPressed: () => Get.back(),
child: const Text('Batal'),
),
ElevatedButton(
onPressed: () {
controller.changePassword(
currentPasswordController.text,
newPasswordController.text,
confirmPasswordController.text,
);
},
style: ElevatedButton.styleFrom(
backgroundColor: AppTheme.primaryColor,
),
child: const Text('Simpan'),
),
],
),
);
}
Widget _buildInfoRow(IconData icon, String label, String value) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icon, size: 18, color: Colors.grey[700]),
const SizedBox(width: 8),
Expanded(
child: Text(
'$label: $value',
style: TextStyle(
fontSize: 14,
color: Colors.grey[700],
),
),
),
],
);
}
}