188 lines
5.6 KiB
Dart
188 lines
5.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
import '../../../routes/app_pages.dart';
|
|
import '../../../widgets/numpad_nominal_widget.dart';
|
|
import '../../../widgets/numpad_pin_widget.dart';
|
|
import '../../../widgets/target_celengan_picker.dart';
|
|
|
|
class RegisterController extends GetxController {
|
|
final namaController = TextEditingController();
|
|
final emailController = TextEditingController();
|
|
final passwordController = TextEditingController();
|
|
final targetCelenganController = TextEditingController();
|
|
final nominalTargetController = TextEditingController();
|
|
final pinController = TextEditingController();
|
|
final confirmPinController = TextEditingController();
|
|
|
|
final isPinVisible = false.obs;
|
|
final isConfirmPinVisible = false.obs;
|
|
final isLoading = false.obs;
|
|
|
|
String _activeField = '';
|
|
|
|
final _auth = FirebaseAuth.instance;
|
|
final _firestore = FirebaseFirestore.instance;
|
|
|
|
@override
|
|
void onClose() {
|
|
namaController.dispose();
|
|
emailController.dispose();
|
|
passwordController.dispose();
|
|
targetCelenganController.dispose();
|
|
nominalTargetController.dispose();
|
|
pinController.dispose();
|
|
confirmPinController.dispose();
|
|
super.onClose();
|
|
}
|
|
|
|
void togglePinVisibility() => isPinVisible.value = !isPinVisible.value;
|
|
void toggleConfirmPinVisibility() =>
|
|
isConfirmPinVisible.value = !isConfirmPinVisible.value;
|
|
|
|
void showTargetPicker(BuildContext context) {
|
|
TargetCelenganPicker.show(
|
|
context,
|
|
selected: targetCelenganController.text.isEmpty
|
|
? null
|
|
: targetCelenganController.text,
|
|
onSelected: (label) => targetCelenganController.text = label,
|
|
);
|
|
}
|
|
|
|
void showNumpad(BuildContext context, String field) {
|
|
_activeField = field;
|
|
final activeCtrl = _getController(field);
|
|
final isPinField = field == 'pin' || field == 'confirmPin';
|
|
|
|
if (isPinField) {
|
|
NumpadPinInputWidget.show(
|
|
context,
|
|
controller: activeCtrl,
|
|
onKey: _onNumpadKey,
|
|
onSimpan: () => Get.back(),
|
|
title: field == 'pin' ? 'Masukkan PIN' : 'Ulangi PIN',
|
|
);
|
|
} else {
|
|
NumpadNominalWidget.show(
|
|
context,
|
|
controller: activeCtrl,
|
|
onKey: _onNumpadKey,
|
|
onSimpan: () => Get.back(),
|
|
);
|
|
}
|
|
}
|
|
|
|
TextEditingController _getController(String field) {
|
|
if (field == 'pin') return pinController;
|
|
if (field == 'confirmPin') return confirmPinController;
|
|
return nominalTargetController;
|
|
}
|
|
|
|
void _onNumpadKey(String key) {
|
|
final ctrl = _getController(_activeField);
|
|
final isPinField = _activeField == 'pin' || _activeField == 'confirmPin';
|
|
|
|
if (key == 'del') {
|
|
if (ctrl.text.isNotEmpty)
|
|
ctrl.text = ctrl.text.substring(0, ctrl.text.length - 1);
|
|
} else {
|
|
if (isPinField && ctrl.text.length >= 6) return;
|
|
ctrl.text += key;
|
|
}
|
|
}
|
|
|
|
/// Parse nominal dari string seperti "Rp 2.500.000" → 2500000
|
|
int _parseNominal(String raw) {
|
|
final cleaned = raw.replaceAll(RegExp(r'[^0-9]'), '');
|
|
return int.tryParse(cleaned) ?? 0;
|
|
}
|
|
|
|
Future<void> simpan() async {
|
|
if (namaController.text.isEmpty ||
|
|
emailController.text.isEmpty ||
|
|
passwordController.text.isEmpty ||
|
|
targetCelenganController.text.isEmpty ||
|
|
nominalTargetController.text.isEmpty ||
|
|
pinController.text.isEmpty ||
|
|
confirmPinController.text.isEmpty) {
|
|
Get.snackbar(
|
|
'Perhatian',
|
|
'Semua field harus diisi!',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.red.shade100,
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (pinController.text != confirmPinController.text) {
|
|
Get.snackbar(
|
|
'Perhatian',
|
|
'PIN tidak cocok!',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.red.shade100,
|
|
);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
isLoading.value = true;
|
|
|
|
// 1. Buat akun di Firebase Auth
|
|
final credential = await _auth.createUserWithEmailAndPassword(
|
|
email: emailController.text.trim(),
|
|
password: passwordController.text.trim(),
|
|
);
|
|
|
|
final uid = credential.user!.uid;
|
|
final now = FieldValue.serverTimestamp();
|
|
|
|
// 2. Simpan ke collection 'users'
|
|
await _firestore.collection('users').doc(uid).set({
|
|
'email': emailController.text.trim(),
|
|
'password': 'hashed_password', // Hanya placeholder, auth sudah handle
|
|
'createdAt': now,
|
|
});
|
|
|
|
// 3. Simpan ke collection 'profil'
|
|
await _firestore.collection('profil').doc(uid).set({
|
|
'userId': uid,
|
|
'nama': namaController.text.trim(),
|
|
'target': targetCelenganController.text.trim(),
|
|
'targetNominal': _parseNominal(nominalTargetController.text),
|
|
'pin': pinController.text,
|
|
'totalSaldo': 0,
|
|
'createdAt': now,
|
|
});
|
|
|
|
Get.snackbar(
|
|
'Berhasil',
|
|
'Akun berhasil dibuat!',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.green.shade100,
|
|
);
|
|
|
|
Get.offAllNamed(Routes.LOGIN);
|
|
} on FirebaseAuthException catch (e) {
|
|
String message = 'Registrasi gagal.';
|
|
if (e.code == 'email-already-in-use') {
|
|
message = 'Email sudah digunakan.';
|
|
} else if (e.code == 'weak-password') {
|
|
message = 'Password terlalu lemah (min. 6 karakter).';
|
|
} else if (e.code == 'invalid-email') {
|
|
message = 'Format email tidak valid.';
|
|
}
|
|
|
|
Get.snackbar(
|
|
'Registrasi Gagal',
|
|
message,
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.red.shade100,
|
|
);
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
}
|