ambil data stok bantuan
This commit is contained in:
11
lib/app/modules/profile/bindings/profile_binding.dart
Normal file
11
lib/app/modules/profile/bindings/profile_binding.dart
Normal file
@ -0,0 +1,11 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:penyaluran_app/app/modules/profile/controllers/profile_controller.dart';
|
||||
|
||||
class ProfileBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<ProfileController>(
|
||||
() => ProfileController(),
|
||||
);
|
||||
}
|
||||
}
|
157
lib/app/modules/profile/controllers/profile_controller.dart
Normal file
157
lib/app/modules/profile/controllers/profile_controller.dart
Normal file
@ -0,0 +1,157 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:penyaluran_app/app/data/models/user_model.dart';
|
||||
import 'package:penyaluran_app/app/services/auth_service.dart';
|
||||
|
||||
class ProfileController extends GetxController {
|
||||
final AuthService _authService = Get.find<AuthService>();
|
||||
|
||||
final Rx<User?> user = Rx<User?>(null);
|
||||
final RxBool isLoading = true.obs;
|
||||
final RxBool isEditing = false.obs;
|
||||
|
||||
// Form controllers
|
||||
late TextEditingController nameController;
|
||||
late TextEditingController emailController;
|
||||
late TextEditingController phoneController;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
nameController = TextEditingController();
|
||||
emailController = TextEditingController();
|
||||
phoneController = TextEditingController();
|
||||
loadUserData();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
nameController.dispose();
|
||||
emailController.dispose();
|
||||
phoneController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> loadUserData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
// Mendapatkan data user dari service
|
||||
final userData = await _authService.getCurrentUser();
|
||||
user.value = userData;
|
||||
|
||||
// Mengisi form controllers dengan data user
|
||||
if (userData != null) {
|
||||
nameController.text = userData.name ?? '';
|
||||
emailController.text = userData.email ?? '';
|
||||
phoneController.text = userData.phone ?? '';
|
||||
}
|
||||
} catch (e) {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal memuat data profil: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void toggleEditMode() {
|
||||
isEditing.value = !isEditing.value;
|
||||
}
|
||||
|
||||
Future<void> updateProfile() async {
|
||||
if (nameController.text.isEmpty) {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Nama tidak boleh kosong',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
try {
|
||||
// Update user data
|
||||
final updatedUser = User(
|
||||
id: user.value?.id,
|
||||
name: nameController.text,
|
||||
email: emailController.text,
|
||||
phone: phoneController.text,
|
||||
role: user.value?.role,
|
||||
token: user.value?.token,
|
||||
);
|
||||
|
||||
// Panggil API untuk update profil
|
||||
await _authService.updateProfile(updatedUser);
|
||||
|
||||
// Refresh data
|
||||
await loadUserData();
|
||||
|
||||
// Keluar dari mode edit
|
||||
isEditing.value = false;
|
||||
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Profil berhasil diperbarui',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal memperbarui profil: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> changePassword(String currentPassword, String newPassword,
|
||||
String confirmPassword) async {
|
||||
if (newPassword != confirmPassword) {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Konfirmasi password tidak sesuai',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
try {
|
||||
// Panggil API untuk ganti password
|
||||
await _authService.changePassword(currentPassword, newPassword);
|
||||
|
||||
Get.back(); // Tutup dialog
|
||||
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Password berhasil diubah',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal mengubah password: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
233
lib/app/modules/profile/views/profile_view.dart
Normal file
233
lib/app/modules/profile/views/profile_view.dart
Normal file
@ -0,0 +1,233 @@
|
||||
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';
|
||||
|
||||
class ProfileView extends GetView<ProfileController> {
|
||||
const ProfileView({Key? key}) : super(key: key);
|
||||
|
||||
@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: [
|
||||
const CircleAvatar(
|
||||
radius: 50,
|
||||
backgroundColor: AppTheme.primaryColor,
|
||||
child: Icon(
|
||||
Icons.person,
|
||||
size: 60,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Obx(() => Text(
|
||||
controller.user.value?.name ?? 'Pengguna',
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
)),
|
||||
const SizedBox(height: 4),
|
||||
Obx(() => Text(
|
||||
controller.user.value?.role?.toUpperCase() ?? 'PENGGUNA',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey[600],
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProfileForm() {
|
||||
return Obx(() {
|
||||
final isEditing = controller.isEditing.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: isEditing,
|
||||
),
|
||||
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,
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
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'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user