1278 lines
46 KiB
Dart
1278 lines
46 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import '../controllers/auth_controller.dart';
|
|
import '../../../theme/app_colors.dart';
|
|
import 'package:intl/intl.dart';
|
|
|
|
class RegistrationView extends GetView<AuthController> {
|
|
const RegistrationView({Key? key}) : super(key: key);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return WillPopScope(
|
|
onWillPop: () async {
|
|
// Reset all registration fields when leaving the page
|
|
controller.resetRegistrationFields();
|
|
return true;
|
|
},
|
|
child: Scaffold(
|
|
body: Stack(
|
|
children: [
|
|
// Background gradient - same as login page
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topRight,
|
|
end: Alignment.bottomLeft,
|
|
colors: [
|
|
AppColors.primaryLight.withOpacity(0.1),
|
|
AppColors.background,
|
|
AppColors.accentLight.withOpacity(0.1),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
|
|
// Pattern overlay - same as login page
|
|
Opacity(
|
|
opacity: 0.03,
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.blue[50], // Temporary solid color
|
|
),
|
|
),
|
|
),
|
|
|
|
// Accent circles - same as login page
|
|
Positioned(
|
|
top: -40,
|
|
right: -20,
|
|
child: Container(
|
|
width: 150,
|
|
height: 150,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
gradient: RadialGradient(
|
|
colors: [
|
|
AppColors.primary.withOpacity(0.2),
|
|
Colors.transparent,
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
Positioned(
|
|
bottom: -50,
|
|
left: -30,
|
|
child: Container(
|
|
width: 180,
|
|
height: 180,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
gradient: RadialGradient(
|
|
colors: [
|
|
AppColors.accent.withOpacity(0.2),
|
|
Colors.transparent,
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// Main content
|
|
SafeArea(
|
|
child: SingleChildScrollView(
|
|
physics: const BouncingScrollPhysics(),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
const SizedBox(height: 20),
|
|
_buildBackButton(),
|
|
const SizedBox(height: 20),
|
|
_buildHeader(),
|
|
const SizedBox(height: 30),
|
|
_buildRegistrationCard(),
|
|
_buildLoginLink(),
|
|
_buildCheckStatusLink(),
|
|
const SizedBox(height: 30),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildBackButton() {
|
|
return Align(
|
|
alignment: Alignment.topLeft,
|
|
child: InkWell(
|
|
onTap: () {
|
|
// Reset all registration fields when going back
|
|
controller.resetRegistrationFields();
|
|
Get.back();
|
|
},
|
|
borderRadius: BorderRadius.circular(50),
|
|
child: Container(
|
|
padding: const EdgeInsets.all(10),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
shape: BoxShape.circle,
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: AppColors.shadow,
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Icon(Icons.arrow_back, size: 20, color: AppColors.primary),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildHeader() {
|
|
return Center(
|
|
child: Hero(
|
|
tag: 'logo',
|
|
child: Image.asset(
|
|
'assets/images/logo.png',
|
|
width: 150,
|
|
height: 150,
|
|
errorBuilder: (context, error, stackTrace) {
|
|
return Icon(
|
|
Icons.apartment_rounded,
|
|
size: 120,
|
|
color: AppColors.primary,
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildRegistrationCard() {
|
|
return Card(
|
|
elevation: 4,
|
|
shadowColor: AppColors.shadow,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(28.0),
|
|
child: Form(
|
|
key: controller.formKey,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Welcome text
|
|
Text(
|
|
'Daftar Akun',
|
|
style: TextStyle(
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.bold,
|
|
color: AppColors.textPrimary,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'Lengkapi data berikut untuk mendaftar',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: AppColors.textSecondary,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
// Account Credentials Section
|
|
Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.primaryLight.withOpacity(0.05),
|
|
borderRadius: BorderRadius.circular(16),
|
|
border: Border.all(
|
|
color: AppColors.primaryLight.withOpacity(0.2),
|
|
width: 1,
|
|
),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Icon(
|
|
Icons.account_circle_outlined,
|
|
color: AppColors.primary,
|
|
size: 20,
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
'Informasi Akun',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
color: AppColors.primary,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Email field
|
|
_buildInputLabel('Email'),
|
|
const SizedBox(height: 8),
|
|
_buildTextField(
|
|
hintText: 'Masukkan email anda',
|
|
prefixIcon: Icons.email_outlined,
|
|
keyboardType: TextInputType.emailAddress,
|
|
onChanged: (value) => controller.email.value = value,
|
|
validator: controller.validateEmail,
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Password field
|
|
_buildInputLabel('Password'),
|
|
const SizedBox(height: 8),
|
|
Obx(
|
|
() => _buildTextField(
|
|
hintText: 'Masukkan password anda',
|
|
prefixIcon: Icons.lock_outline,
|
|
obscureText: !controller.isPasswordVisible.value,
|
|
onChanged: (value) => controller.password.value = value,
|
|
validator: controller.validatePassword,
|
|
suffixIcon: IconButton(
|
|
icon: Icon(
|
|
controller.isPasswordVisible.value
|
|
? Icons.visibility
|
|
: Icons.visibility_off,
|
|
color: AppColors.iconGrey,
|
|
),
|
|
onPressed: controller.togglePasswordVisibility,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Confirm Password field
|
|
_buildInputLabel('Konfirmasi Password'),
|
|
const SizedBox(height: 8),
|
|
Obx(
|
|
() => _buildTextField(
|
|
controller: controller.confirmPasswordController,
|
|
hintText: 'Masukkan ulang password anda',
|
|
prefixIcon: Icons.lock_outline,
|
|
obscureText: !controller.isConfirmPasswordVisible.value,
|
|
validator: controller.validateConfirmPassword,
|
|
suffixIcon: IconButton(
|
|
icon: Icon(
|
|
controller.isConfirmPasswordVisible.value
|
|
? Icons.visibility
|
|
: Icons.visibility_off,
|
|
color: AppColors.iconGrey,
|
|
),
|
|
onPressed: controller.toggleConfirmPasswordVisibility,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 24),
|
|
|
|
// Personal Data Section
|
|
Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.accentLight.withOpacity(0.05),
|
|
borderRadius: BorderRadius.circular(16),
|
|
border: Border.all(
|
|
color: AppColors.accentLight.withOpacity(0.2),
|
|
width: 1,
|
|
),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Icon(
|
|
Icons.person_outline,
|
|
color: AppColors.accent,
|
|
size: 20,
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
'Data Diri',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
color: AppColors.accent,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Name field
|
|
_buildInputLabel('Nama Lengkap'),
|
|
const SizedBox(height: 8),
|
|
_buildTextField(
|
|
controller: controller.nameController,
|
|
hintText: 'Masukkan nama lengkap anda',
|
|
prefixIcon: Icons.person_outline,
|
|
validator: controller.validateName,
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// NIK field
|
|
_buildInputLabel('NIK'),
|
|
const SizedBox(height: 8),
|
|
_buildTextField(
|
|
hintText: 'Masukkan NIK anda',
|
|
prefixIcon: Icons.credit_card_outlined,
|
|
keyboardType: TextInputType.number,
|
|
onChanged: (value) => controller.nik.value = value,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'NIK tidak boleh kosong';
|
|
}
|
|
if (value.length != 16) {
|
|
return 'NIK harus 16 digit';
|
|
}
|
|
if (!RegExp(r'^[0-9]+$').hasMatch(value)) {
|
|
return 'NIK hanya boleh berisi angka';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Tanggal Lahir field
|
|
_buildInputLabel('Tanggal Lahir'),
|
|
const SizedBox(height: 8),
|
|
Builder(builder: (context) => _buildDateField(context)),
|
|
const SizedBox(height: 16),
|
|
|
|
// Phone field
|
|
_buildInputLabel('No HP'),
|
|
const SizedBox(height: 8),
|
|
_buildTextField(
|
|
hintText: 'Masukkan nomor HP anda',
|
|
prefixIcon: Icons.phone_outlined,
|
|
keyboardType: TextInputType.phone,
|
|
onChanged:
|
|
(value) => controller.phoneNumber.value = value,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'No HP tidak boleh kosong';
|
|
}
|
|
if (!value.startsWith('08')) {
|
|
return 'Nomor HP harus diawali dengan 08';
|
|
}
|
|
if (value.length < 10 || value.length > 13) {
|
|
return 'Nomor HP harus antara 10-13 digit';
|
|
}
|
|
if (!RegExp(r'^[0-9]+$').hasMatch(value)) {
|
|
return 'Nomor HP hanya boleh berisi angka';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// RT/RW field
|
|
_buildInputLabel('RT/RW'),
|
|
const SizedBox(height: 8),
|
|
_buildTextField(
|
|
hintText: 'Contoh: 001/002',
|
|
prefixIcon: Icons.home_work_outlined,
|
|
onChanged: (value) => controller.rtRw.value = value,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'RT/RW tidak boleh kosong';
|
|
}
|
|
if (!RegExp(r'^\d{1,3}\/\d{1,3}$').hasMatch(value)) {
|
|
return 'Format RT/RW tidak valid (contoh: 001/002)';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Kelurahan/Desa field
|
|
_buildInputLabel('Kelurahan/Desa'),
|
|
const SizedBox(height: 8),
|
|
_buildTextField(
|
|
hintText: 'Masukkan kelurahan/desa anda',
|
|
prefixIcon: Icons.location_city_outlined,
|
|
onChanged: (value) => controller.kelurahan.value = value,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'Kelurahan/Desa tidak boleh kosong';
|
|
}
|
|
if (value.length < 3) {
|
|
return 'Kelurahan/Desa minimal 3 karakter';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Kecamatan field
|
|
_buildInputLabel('Kecamatan'),
|
|
const SizedBox(height: 8),
|
|
_buildTextField(
|
|
hintText: 'Masukkan kecamatan anda',
|
|
prefixIcon: Icons.location_on_outlined,
|
|
onChanged: (value) => controller.kecamatan.value = value,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'Kecamatan tidak boleh kosong';
|
|
}
|
|
if (value.length < 3) {
|
|
return 'Kecamatan minimal 3 karakter';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Address field
|
|
_buildInputLabel('Alamat Lengkap'),
|
|
const SizedBox(height: 8),
|
|
_buildTextField(
|
|
hintText: 'Masukkan alamat lengkap anda',
|
|
prefixIcon: Icons.home_outlined,
|
|
onChanged:
|
|
(value) => controller.alamatLengkap.value = value,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'Alamat lengkap tidak boleh kosong';
|
|
}
|
|
if (value.length < 5) {
|
|
return 'Alamat terlalu pendek, minimal 5 karakter';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 24),
|
|
|
|
// Important info
|
|
_buildImportantInfo(),
|
|
const SizedBox(height: 24),
|
|
|
|
// Register button
|
|
Obx(
|
|
() => SizedBox(
|
|
width: double.infinity,
|
|
height: 56,
|
|
child: ElevatedButton(
|
|
onPressed:
|
|
controller.isLoading.value
|
|
? null
|
|
: controller.registerUser,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: AppColors.primary,
|
|
foregroundColor: AppColors.buttonText,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
elevation: controller.isLoading.value ? 0 : 2,
|
|
shadowColor: AppColors.primary.withOpacity(0.4),
|
|
),
|
|
child:
|
|
controller.isLoading.value
|
|
? const SizedBox(
|
|
height: 24,
|
|
width: 24,
|
|
child: CircularProgressIndicator(
|
|
color: Colors.white,
|
|
strokeWidth: 2,
|
|
),
|
|
)
|
|
: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Text(
|
|
'Daftar',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
letterSpacing: 0.5,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
const Icon(Icons.arrow_forward, size: 18),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// Error message
|
|
Obx(
|
|
() =>
|
|
controller.errorMessage.value.isNotEmpty
|
|
? Container(
|
|
margin: const EdgeInsets.only(top: 16),
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.errorLight,
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
Icons.error_outline,
|
|
color: AppColors.error,
|
|
size: 20,
|
|
),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(
|
|
controller.errorMessage.value,
|
|
style: TextStyle(
|
|
color: AppColors.error,
|
|
fontSize: 13,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
)
|
|
: const SizedBox.shrink(),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildInputLabel(String label) {
|
|
return Text(
|
|
label,
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.w600,
|
|
color: AppColors.textPrimary,
|
|
fontSize: 15,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildTextField({
|
|
TextEditingController? controller,
|
|
required String hintText,
|
|
required IconData prefixIcon,
|
|
TextInputType keyboardType = TextInputType.text,
|
|
bool obscureText = false,
|
|
Widget? suffixIcon,
|
|
Function(String)? onChanged,
|
|
String? Function(String?)? validator,
|
|
}) {
|
|
return TextFormField(
|
|
controller: controller,
|
|
keyboardType: keyboardType,
|
|
obscureText: obscureText,
|
|
onChanged: onChanged,
|
|
validator: validator,
|
|
style: TextStyle(fontSize: 16, color: AppColors.textPrimary),
|
|
decoration: InputDecoration(
|
|
hintText: hintText,
|
|
hintStyle: TextStyle(color: AppColors.textLight),
|
|
prefixIcon: Icon(prefixIcon, color: AppColors.iconGrey, size: 22),
|
|
suffixIcon: suffixIcon,
|
|
filled: true,
|
|
fillColor: AppColors.inputBackground,
|
|
contentPadding: const EdgeInsets.symmetric(vertical: 16),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
borderSide: BorderSide(color: AppColors.primary, width: 1.5),
|
|
),
|
|
errorBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
borderSide: BorderSide(color: AppColors.error, width: 1.5),
|
|
),
|
|
focusedErrorBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
borderSide: BorderSide(color: AppColors.error, width: 1.5),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildImportantInfo() {
|
|
return Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.warningLight,
|
|
borderRadius: BorderRadius.circular(14),
|
|
),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Icon(Icons.info_outline, size: 20, color: AppColors.warning),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Text(
|
|
'Setelah melakukan pendaftaran, silahkan simpan kode registrasi untuk cek status pendaftaran',
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
color: AppColors.textPrimary,
|
|
height: 1.4,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildLoginLink() {
|
|
return Center(
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
"Sudah punya akun?",
|
|
style: TextStyle(color: AppColors.textSecondary),
|
|
),
|
|
TextButton(
|
|
onPressed: () {
|
|
// Reset all registration fields when going back to login
|
|
controller.resetRegistrationFields();
|
|
Get.back();
|
|
},
|
|
style: TextButton.styleFrom(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
),
|
|
child: Text(
|
|
'Masuk',
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
color: AppColors.primary,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// Add button to check registration status
|
|
Widget _buildCheckStatusLink() {
|
|
return Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(top: 8.0),
|
|
child: TextButton(
|
|
onPressed: () => _showCheckStatusDialog(Get.context!),
|
|
style: TextButton.styleFrom(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(Icons.search, size: 18, color: AppColors.accent),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
'Cek Status Pendaftaran',
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
color: AppColors.accent,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Show dialog to check registration status
|
|
void _showCheckStatusDialog(BuildContext context) {
|
|
final TextEditingController codeController = TextEditingController();
|
|
final TextEditingController emailController = TextEditingController();
|
|
final authController = Get.find<AuthController>();
|
|
|
|
showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return Dialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Dialog header
|
|
Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.accent.withOpacity(0.1),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
Icons.search,
|
|
color: AppColors.accent,
|
|
size: 20,
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Text(
|
|
'Cek Status Pendaftaran',
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
color: AppColors.textPrimary,
|
|
),
|
|
),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.close, size: 20),
|
|
onPressed: () => Navigator.pop(context),
|
|
padding: EdgeInsets.zero,
|
|
constraints: const BoxConstraints(),
|
|
splashRadius: 24,
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Registration code field
|
|
Text(
|
|
'Kode Registrasi',
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.w600,
|
|
color: AppColors.textPrimary,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
TextField(
|
|
controller: codeController,
|
|
decoration: InputDecoration(
|
|
hintText: 'Masukkan kode registrasi',
|
|
hintStyle: TextStyle(color: AppColors.textLight),
|
|
prefixIcon: Icon(
|
|
Icons.confirmation_number_outlined,
|
|
color: AppColors.iconGrey,
|
|
size: 22,
|
|
),
|
|
filled: true,
|
|
fillColor: AppColors.inputBackground,
|
|
contentPadding: const EdgeInsets.symmetric(vertical: 16),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
borderSide: BorderSide(
|
|
color: AppColors.accent,
|
|
width: 1.5,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Email field
|
|
Text(
|
|
'Email',
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.w600,
|
|
color: AppColors.textPrimary,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
TextField(
|
|
controller: emailController,
|
|
keyboardType: TextInputType.emailAddress,
|
|
decoration: InputDecoration(
|
|
hintText: 'Masukkan email yang terdaftar',
|
|
hintStyle: TextStyle(color: AppColors.textLight),
|
|
prefixIcon: Icon(
|
|
Icons.email_outlined,
|
|
color: AppColors.iconGrey,
|
|
size: 22,
|
|
),
|
|
filled: true,
|
|
fillColor: AppColors.inputBackground,
|
|
contentPadding: const EdgeInsets.symmetric(vertical: 16),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
borderSide: BorderSide(
|
|
color: AppColors.accent,
|
|
width: 1.5,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
// Submit button
|
|
SizedBox(
|
|
width: double.infinity,
|
|
height: 48,
|
|
child: ElevatedButton(
|
|
onPressed: () async {
|
|
// Validasi input
|
|
if (codeController.text.isEmpty ||
|
|
emailController.text.isEmpty) {
|
|
Get.snackbar(
|
|
'Error',
|
|
'Kode registrasi dan Email harus diisi',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: AppColors.errorLight,
|
|
colorText: AppColors.error,
|
|
icon: Icon(
|
|
Icons.error_outline,
|
|
color: AppColors.error,
|
|
),
|
|
duration: const Duration(seconds: 3),
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Simpan nilai input untuk digunakan nanti
|
|
final String registerId = codeController.text.trim();
|
|
final String email = emailController.text.trim();
|
|
|
|
print(
|
|
'Checking registration status for ID: $registerId, Email: $email',
|
|
);
|
|
|
|
// Tutup dialog input
|
|
Navigator.pop(context);
|
|
|
|
// Tampilkan loading indicator
|
|
Get.dialog(
|
|
const Center(child: CircularProgressIndicator()),
|
|
barrierDismissible: false,
|
|
);
|
|
|
|
try {
|
|
// Cek status pendaftaran
|
|
final result = await authController
|
|
.checkRegistrationStatus(registerId, email);
|
|
|
|
// Debug log
|
|
print('Registration status check result: $result');
|
|
|
|
// Tutup dialog loading
|
|
Get.back();
|
|
|
|
// Periksa hasil query
|
|
if (result != null && result.isNotEmpty) {
|
|
print('Valid result found, showing status dialog');
|
|
|
|
// Tambahkan delay kecil untuk memastikan UI diperbarui dengan benar
|
|
// Ini membantu memastikan dialog status muncul setelah dialog loading ditutup
|
|
Future.delayed(const Duration(milliseconds: 100), () {
|
|
_showRegistrationStatusDialog(result);
|
|
});
|
|
} else {
|
|
print('No result found or empty result');
|
|
|
|
// Tampilkan pesan error
|
|
Get.snackbar(
|
|
'Tidak Ditemukan',
|
|
'Data pendaftaran tidak ditemukan. Pastikan kode registrasi dan email yang dimasukkan benar.',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: AppColors.errorLight,
|
|
colorText: AppColors.error,
|
|
icon: Icon(
|
|
Icons.error_outline,
|
|
color: AppColors.error,
|
|
),
|
|
duration: const Duration(seconds: 4),
|
|
);
|
|
}
|
|
} catch (e) {
|
|
// Tutup dialog loading jika terjadi error
|
|
Get.back();
|
|
|
|
print('Error checking registration status: $e');
|
|
|
|
// Tampilkan pesan error
|
|
Get.snackbar(
|
|
'Error',
|
|
'Terjadi kesalahan saat memeriksa status. Silakan coba lagi nanti.',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: AppColors.errorLight,
|
|
colorText: AppColors.error,
|
|
icon: Icon(
|
|
Icons.error_outline,
|
|
color: AppColors.error,
|
|
),
|
|
duration: const Duration(seconds: 4),
|
|
);
|
|
}
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: AppColors.accent,
|
|
foregroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
),
|
|
elevation: 0,
|
|
),
|
|
child: const Text(
|
|
'Cek Status',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
// Show dialog with registration status details
|
|
void _showRegistrationStatusDialog(Map<String, dynamic> data) {
|
|
// Log data yang diterima untuk debugging
|
|
print('Showing registration status dialog with data: $data');
|
|
|
|
// Validasi data
|
|
if (data.isEmpty) {
|
|
print('Error: Empty data received in _showRegistrationStatusDialog');
|
|
Get.snackbar(
|
|
'Error',
|
|
'Data status pendaftaran tidak valid',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: AppColors.errorLight,
|
|
colorText: AppColors.error,
|
|
duration: const Duration(seconds: 3),
|
|
);
|
|
return;
|
|
}
|
|
|
|
final String status = data['status'] ?? 'unknown';
|
|
final String namaLengkap = data['nama_lengkap'] ?? '';
|
|
final String keterangan = data['keterangan'] ?? '';
|
|
|
|
print('Processing status: $status, nama: $namaLengkap');
|
|
|
|
// Define status color and message based on status
|
|
Color statusColor;
|
|
String statusMessage;
|
|
IconData statusIcon;
|
|
|
|
switch (status) {
|
|
case 'pending':
|
|
statusColor = AppColors.warning;
|
|
statusMessage =
|
|
keterangan.isNotEmpty
|
|
? keterangan
|
|
: 'Pendaftaran Anda sedang dalam proses verifikasi oleh petugas. Mohon tunggu konfirmasi lebih lanjut.';
|
|
statusIcon = Icons.hourglass_top;
|
|
break;
|
|
case 'active':
|
|
statusColor = AppColors.success;
|
|
statusMessage =
|
|
keterangan.isNotEmpty
|
|
? keterangan
|
|
: 'Pendaftaran Anda telah disetujui. Anda dapat login menggunakan email dan password yang telah didaftarkan.';
|
|
statusIcon = Icons.check_circle;
|
|
break;
|
|
case 'dibatalkan':
|
|
statusColor = AppColors.error;
|
|
statusMessage =
|
|
keterangan.isNotEmpty
|
|
? keterangan
|
|
: 'Maaf, pendaftaran Anda ditolak. Silakan hubungi admin untuk informasi lebih lanjut.';
|
|
statusIcon = Icons.cancel;
|
|
break;
|
|
case 'suspended':
|
|
statusColor = AppColors.error;
|
|
statusMessage =
|
|
keterangan.isNotEmpty
|
|
? keterangan
|
|
: 'Akun Anda saat ini dinonaktifkan. Silakan hubungi admin untuk informasi lebih lanjut.';
|
|
statusIcon = Icons.block;
|
|
break;
|
|
default:
|
|
statusColor = AppColors.textSecondary;
|
|
statusMessage =
|
|
keterangan.isNotEmpty
|
|
? keterangan
|
|
: 'Status pendaftaran tidak diketahui. Silakan hubungi admin untuk informasi lebih lanjut.';
|
|
statusIcon = Icons.help;
|
|
}
|
|
|
|
print('Preparing to show dialog with status: $status, color: $statusColor');
|
|
|
|
// Gunakan Get.dialog sebagai pengganti showDialog untuk menghindari masalah context
|
|
Get.dialog(
|
|
Dialog(
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Container(
|
|
width: 80,
|
|
height: 80,
|
|
decoration: BoxDecoration(
|
|
color: statusColor.withOpacity(0.1),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(statusIcon, color: statusColor, size: 40),
|
|
),
|
|
const SizedBox(height: 24),
|
|
Text(
|
|
'Status Pendaftaran',
|
|
style: TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
color: AppColors.textPrimary,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
namaLengkap,
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w500,
|
|
color: AppColors.textSecondary,
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 16,
|
|
vertical: 8,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: statusColor.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(30),
|
|
),
|
|
child: Text(
|
|
status.toUpperCase(),
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.bold,
|
|
color: statusColor,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
Text(
|
|
statusMessage,
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(
|
|
fontSize: 15,
|
|
color: AppColors.textSecondary,
|
|
height: 1.5,
|
|
),
|
|
),
|
|
|
|
// Tampilkan informasi tambahan jika ada
|
|
if (data.containsKey('tanggal_update') &&
|
|
data['tanggal_update'] != null) ...[
|
|
const SizedBox(height: 16),
|
|
Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.infoLight.withOpacity(0.3),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Icon(
|
|
Icons.info_outline,
|
|
size: 16,
|
|
color: AppColors.info,
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
'Informasi Tambahan',
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 14,
|
|
color: AppColors.info,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'Terakhir diperbarui: ${_formatDate(data['tanggal_update'])}',
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
color: AppColors.textSecondary,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
|
|
const SizedBox(height: 24),
|
|
SizedBox(
|
|
width: double.infinity,
|
|
child: ElevatedButton(
|
|
onPressed: () => Get.back(),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: statusColor,
|
|
foregroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
),
|
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
),
|
|
child: const Text(
|
|
'Tutup',
|
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
barrierDismissible: true,
|
|
);
|
|
|
|
print('Dialog setup completed');
|
|
}
|
|
|
|
// New method to build date picker field
|
|
Widget _buildDateField(BuildContext context) {
|
|
return GestureDetector(
|
|
onTap: () async {
|
|
final DateTime? picked = await showDatePicker(
|
|
context: context,
|
|
initialDate: DateTime.now().subtract(
|
|
const Duration(days: 365 * 18),
|
|
), // Default to 18 years ago
|
|
firstDate: DateTime(1940),
|
|
lastDate: DateTime.now(),
|
|
builder: (BuildContext context, Widget? child) {
|
|
return Theme(
|
|
data: ThemeData.light().copyWith(
|
|
colorScheme: ColorScheme.light(
|
|
primary: AppColors.primary,
|
|
onPrimary: Colors.white,
|
|
surface: Colors.white,
|
|
onSurface: AppColors.textPrimary,
|
|
),
|
|
dialogBackgroundColor: Colors.white,
|
|
),
|
|
child: child!,
|
|
);
|
|
},
|
|
);
|
|
if (picked != null) {
|
|
controller.tanggalLahir.value = picked;
|
|
}
|
|
},
|
|
child: Obx(() {
|
|
final selectedDate = controller.tanggalLahir.value;
|
|
final displayText =
|
|
selectedDate != null
|
|
? '${selectedDate.day.toString().padLeft(2, '0')}-${selectedDate.month.toString().padLeft(2, '0')}-${selectedDate.year}'
|
|
: 'Pilih tanggal lahir';
|
|
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.inputBackground,
|
|
borderRadius: BorderRadius.circular(14),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
|
child: Icon(
|
|
Icons.calendar_today_outlined,
|
|
color: AppColors.iconGrey,
|
|
size: 22,
|
|
),
|
|
),
|
|
Text(
|
|
displayText,
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
color:
|
|
selectedDate != null
|
|
? AppColors.textPrimary
|
|
: AppColors.textLight,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}),
|
|
);
|
|
}
|
|
|
|
String _formatDate(dynamic date) {
|
|
if (date is String) {
|
|
try {
|
|
final DateTime parsedDate = DateTime.parse(date);
|
|
return DateFormat('dd MMM yyyy, HH:mm').format(parsedDate);
|
|
} catch (e) {
|
|
return date;
|
|
}
|
|
} else if (date is num) {
|
|
final DateTime parsedDate = DateTime.fromMillisecondsSinceEpoch(
|
|
date.toInt() * 1000,
|
|
);
|
|
return DateFormat('dd MMM yyyy, HH:mm').format(parsedDate);
|
|
} else if (date is DateTime) {
|
|
return DateFormat('dd MMM yyyy, HH:mm').format(date);
|
|
} else {
|
|
return "Format tanggal tidak diketahui";
|
|
}
|
|
}
|
|
}
|