mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-10 09:22:21 +00:00
update addrses in register
This commit is contained in:
@ -4,10 +4,10 @@ import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class CreateAddressRequest (
|
||||
@SerializedName("latitude")
|
||||
val lat: Double,
|
||||
val lat: Double? = null,
|
||||
|
||||
@SerializedName("longitude")
|
||||
val long: Double,
|
||||
val long: Double? = null,
|
||||
|
||||
@SerializedName("street")
|
||||
val street: String,
|
||||
|
@ -1,7 +1,10 @@
|
||||
package com.alya.ecommerce_serang.data.api.dto
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class RegisterRequest (
|
||||
val name: String?,
|
||||
val email: String?,
|
||||
@ -15,4 +18,4 @@ data class RegisterRequest (
|
||||
val image: String? = null,
|
||||
|
||||
val otp: String? = null
|
||||
)
|
||||
): Parcelable
|
@ -14,6 +14,7 @@ import com.alya.ecommerce_serang.data.api.response.auth.HasStoreResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.ListStoreTypeResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.LoginResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.OtpResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.RegisterResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.RegisterStoreResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.VerifRegisterResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.ListCityResponse
|
||||
@ -59,12 +60,12 @@ class UserRepository(private val apiService: ApiService) {
|
||||
return if (response.isSuccessful) response.body() else null
|
||||
}
|
||||
|
||||
suspend fun registerUser(request: RegisterRequest): String {
|
||||
suspend fun registerUser(request: RegisterRequest): RegisterResponse {
|
||||
val response = apiService.register(request) // API call
|
||||
|
||||
if (response.isSuccessful) {
|
||||
val responseBody = response.body() ?: throw Exception("Empty response body")
|
||||
return responseBody.message // Get the message from RegisterResponse
|
||||
return responseBody // Get the message from RegisterResponse
|
||||
} else {
|
||||
throw Exception("Registration failed: ${response.errorBody()?.string()}")
|
||||
}
|
||||
|
@ -1,49 +1,39 @@
|
||||
package com.alya.ecommerce_serang.ui.auth
|
||||
|
||||
import android.app.DatePickerDialog
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.VerifRegisReq
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import com.alya.ecommerce_serang.databinding.ActivityRegisterBinding
|
||||
import com.alya.ecommerce_serang.ui.MainActivity
|
||||
import com.alya.ecommerce_serang.ui.auth.fragments.RegisterStep1Fragment
|
||||
import com.alya.ecommerce_serang.ui.auth.fragments.RegisterStep2Fragment
|
||||
import com.alya.ecommerce_serang.ui.auth.fragments.RegisterStep3Fragment
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.alya.ecommerce_serang.utils.viewmodel.RegisterViewModel
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
|
||||
class RegisterActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityRegisterBinding
|
||||
private lateinit var sessionManager: SessionManager
|
||||
|
||||
private var isEmailValid = false
|
||||
private var isPhoneValid = false
|
||||
|
||||
// Track which validation was last performed
|
||||
private var lastCheckField = ""
|
||||
|
||||
// Counter for signup validation
|
||||
private var signupValidationsComplete = 0
|
||||
private var signupInProgress = false
|
||||
|
||||
private val registerViewModel: RegisterViewModel by viewModels{
|
||||
BaseViewModelFactory {
|
||||
val apiService = ApiConfig.getUnauthenticatedApiService()
|
||||
val orderRepository = OrderRepository(apiService)
|
||||
val userRepository = UserRepository(apiService)
|
||||
RegisterViewModel(userRepository)
|
||||
RegisterViewModel(userRepository, orderRepository, this)
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,264 +78,27 @@ class RegisterActivity : AppCompatActivity() {
|
||||
windowInsets
|
||||
}
|
||||
|
||||
setupObservers()
|
||||
|
||||
// Set up field validations
|
||||
setupFieldValidations()
|
||||
|
||||
binding.btnSignup.setOnClickListener {
|
||||
handleSignUp()
|
||||
}
|
||||
|
||||
binding.tvLoginAlt.setOnClickListener {
|
||||
val intent = Intent(this, LoginActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.etBirthDate.setOnClickListener {
|
||||
showDatePicker()
|
||||
if (savedInstanceState == null) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_container, RegisterStep1Fragment.newInstance())
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFieldValidations() {
|
||||
// Validate email when focus changes
|
||||
binding.etEmail.setOnFocusChangeListener { _, hasFocus ->
|
||||
if (!hasFocus) {
|
||||
val email = binding.etEmail.text.toString()
|
||||
if (email.isNotEmpty()) {
|
||||
validateEmail(email, false)
|
||||
}
|
||||
}
|
||||
// Function to navigate to the next fragment
|
||||
fun navigateToStep(step: Int, userData: RegisterRequest?) {
|
||||
val fragment = when (step) {
|
||||
1 -> RegisterStep1Fragment.newInstance()
|
||||
2 -> RegisterStep2Fragment.newInstance(userData)
|
||||
3 -> RegisterStep3Fragment.newInstance()
|
||||
else -> null
|
||||
}
|
||||
|
||||
// Validate phone when focus changes
|
||||
binding.etNumberPhone.setOnFocusChangeListener { _, hasFocus ->
|
||||
if (!hasFocus) {
|
||||
val phone = binding.etNumberPhone.text.toString()
|
||||
if (phone.isNotEmpty()) {
|
||||
validatePhone(phone, false)
|
||||
}
|
||||
}
|
||||
fragment?.let {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_container, it)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateEmail(email: String, isSignup: Boolean) {
|
||||
lastCheckField = "email"
|
||||
Log.d("RegisterActivity", "Validating email: $email (signup: $isSignup)")
|
||||
|
||||
val checkValueEmail = VerifRegisReq(
|
||||
fieldRegis = "email",
|
||||
valueRegis = email
|
||||
)
|
||||
registerViewModel.checkValueReg(checkValueEmail)
|
||||
}
|
||||
|
||||
private fun validatePhone(phone: String, isSignup: Boolean) {
|
||||
lastCheckField = "phone"
|
||||
Log.d("RegisterActivity", "Validating phone: $phone (signup: $isSignup)")
|
||||
|
||||
val checkValuePhone = VerifRegisReq(
|
||||
fieldRegis = "phone",
|
||||
valueRegis = phone
|
||||
)
|
||||
registerViewModel.checkValueReg(checkValuePhone)
|
||||
}
|
||||
|
||||
private fun setupObservers() {
|
||||
|
||||
registerViewModel.checkValue.observe(this) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> {
|
||||
// Show loading if needed
|
||||
}
|
||||
is Result.Success -> {
|
||||
val isValid = (result.data as? Boolean) ?: false
|
||||
|
||||
when (lastCheckField) {
|
||||
"email" -> {
|
||||
isEmailValid = isValid
|
||||
if (!isValid) {
|
||||
Toast.makeText(this, "Email is already registered", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Log.d("RegisterActivity", "Email is valid")
|
||||
}
|
||||
}
|
||||
"phone" -> {
|
||||
isPhoneValid = isValid
|
||||
if (!isValid) {
|
||||
Toast.makeText(this, "Phone number is already registered", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Log.d("RegisterActivity", "Phone is valid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we're in signup process
|
||||
if (signupInProgress) {
|
||||
signupValidationsComplete++
|
||||
|
||||
// Check if both validations completed
|
||||
if (signupValidationsComplete >= 2) {
|
||||
signupInProgress = false
|
||||
signupValidationsComplete = 0
|
||||
|
||||
// If both validations passed, request OTP
|
||||
if (isEmailValid && isPhoneValid) {
|
||||
requestOtp()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is Result.Error -> {
|
||||
val fieldType = if (lastCheckField == "email") "Email" else "Phone"
|
||||
Toast.makeText(this, "$fieldType validation failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
|
||||
// Mark validation as invalid
|
||||
if (lastCheckField == "email") {
|
||||
isEmailValid = false
|
||||
} else if (lastCheckField == "phone") {
|
||||
isPhoneValid = false
|
||||
}
|
||||
|
||||
// Update signup validation counter if in signup process
|
||||
if (signupInProgress) {
|
||||
signupValidationsComplete++
|
||||
|
||||
// Check if both validations completed
|
||||
if (signupValidationsComplete >= 2) {
|
||||
signupInProgress = false
|
||||
signupValidationsComplete = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Log.e("RegisterActivity", "Unexpected result type: $result")
|
||||
}
|
||||
}
|
||||
}
|
||||
registerViewModel.otpState.observe(this) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> {
|
||||
binding.progressBarOtp.visibility = android.view.View.VISIBLE
|
||||
}
|
||||
is Result.Success -> {
|
||||
binding.progressBarOtp.visibility = android.view.View.GONE
|
||||
Log.d("RegisterActivity", "OTP sent successfully. Showing OTP dialog.")
|
||||
|
||||
// Create user data before showing OTP dialog
|
||||
val userData = createUserData()
|
||||
|
||||
// Show OTP dialog
|
||||
val otpBottomSheet = OtpBottomSheetDialog(userData) { fullUserData ->
|
||||
Log.d("RegisterActivity", "OTP entered successfully. Proceeding with registration.")
|
||||
registerViewModel.registerUser(fullUserData)
|
||||
}
|
||||
otpBottomSheet.show(supportFragmentManager, "OtpBottomSheet")
|
||||
}
|
||||
is Result.Error -> {
|
||||
binding.progressBarOtp.visibility = android.view.View.GONE
|
||||
Toast.makeText(this, "OTP Request Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
else -> {
|
||||
Log.e("RegisterActivity", "Unexpected result type: $result")
|
||||
}
|
||||
}
|
||||
}
|
||||
registerViewModel.registerState.observe(this) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> {
|
||||
// Show loading indicator for registration
|
||||
binding.progressBarRegister.visibility = android.view.View.VISIBLE
|
||||
}
|
||||
is Result.Success -> {
|
||||
// Hide loading indicator and show success message
|
||||
binding.progressBarRegister.visibility = android.view.View.GONE
|
||||
Toast.makeText(this, result.data, Toast.LENGTH_SHORT).show()
|
||||
val intent = Intent(this, LoginActivity::class.java)
|
||||
startActivity(intent)
|
||||
// Navigate to another screen if needed
|
||||
}
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
|
||||
// Hide loading indicator and show error message
|
||||
binding.progressBarRegister.visibility = android.view.View.GONE
|
||||
Toast.makeText(this, "Registration Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSignUp() {
|
||||
// Basic validation first
|
||||
val email = binding.etEmail.text.toString()
|
||||
val password = binding.etPassword.text.toString()
|
||||
val confirmPassword = binding.etConfirmPassword.text.toString()
|
||||
val phone = binding.etNumberPhone.text.toString()
|
||||
val username = binding.etUsername.text.toString()
|
||||
val name = binding.etFullname.text.toString()
|
||||
val birthDate = binding.etBirthDate.text.toString()
|
||||
|
||||
// Check if fields are filled
|
||||
if (email.isEmpty() || password.isEmpty() || confirmPassword.isEmpty() ||
|
||||
phone.isEmpty() || username.isEmpty() || name.isEmpty() || birthDate.isEmpty()) {
|
||||
Toast.makeText(this, "Please fill all required fields", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
// Check if passwords match
|
||||
if (password != confirmPassword) {
|
||||
Toast.makeText(this, "Passwords do not match", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
// If both validations are already done and successful, just request OTP
|
||||
if (isEmailValid && isPhoneValid) {
|
||||
requestOtp()
|
||||
return
|
||||
}
|
||||
|
||||
// Reset validation counters
|
||||
signupInProgress = true
|
||||
signupValidationsComplete = 0
|
||||
|
||||
// Start validations in parallel
|
||||
validateEmail(email, true)
|
||||
validatePhone(phone, true)
|
||||
}
|
||||
|
||||
private fun requestOtp() {
|
||||
val email = binding.etEmail.text.toString()
|
||||
Log.d("RegisterActivity", "Requesting OTP for email: $email")
|
||||
registerViewModel.requestOtp(email)
|
||||
}
|
||||
|
||||
private fun createUserData(): RegisterRequest {
|
||||
// Get all form values
|
||||
val birthDate = binding.etBirthDate.text.toString()
|
||||
val email = binding.etEmail.text.toString()
|
||||
val password = binding.etPassword.text.toString()
|
||||
val phone = binding.etNumberPhone.text.toString()
|
||||
val username = binding.etUsername.text.toString()
|
||||
val name = binding.etFullname.text.toString()
|
||||
val image = null
|
||||
|
||||
// Create and return user data object
|
||||
return RegisterRequest(name, email, password, username, phone, birthDate, image)
|
||||
}
|
||||
|
||||
private fun showDatePicker() {
|
||||
val calendar = Calendar.getInstance()
|
||||
val year = calendar.get(Calendar.YEAR)
|
||||
val month = calendar.get(Calendar.MONTH)
|
||||
val day = calendar.get(Calendar.DAY_OF_MONTH)
|
||||
|
||||
DatePickerDialog(
|
||||
this,
|
||||
{ _, selectedYear, selectedMonth, selectedDay ->
|
||||
calendar.set(selectedYear, selectedMonth, selectedDay)
|
||||
val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||
binding.etBirthDate.setText(sdf.format(calendar.time))
|
||||
},
|
||||
year, month, day
|
||||
).show()
|
||||
}
|
||||
}
|
@ -0,0 +1,268 @@
|
||||
package com.alya.ecommerce_serang.ui.auth.fragments
|
||||
|
||||
import android.app.DatePickerDialog
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.VerifRegisReq
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import com.alya.ecommerce_serang.databinding.FragmentRegisterStep1Binding
|
||||
import com.alya.ecommerce_serang.ui.auth.LoginActivity
|
||||
import com.alya.ecommerce_serang.ui.auth.RegisterActivity
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.viewmodel.RegisterViewModel
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
|
||||
class RegisterStep1Fragment : Fragment() {
|
||||
private var _binding: FragmentRegisterStep1Binding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val registerViewModel: RegisterViewModel by activityViewModels {
|
||||
BaseViewModelFactory {
|
||||
val apiService = ApiConfig.getUnauthenticatedApiService()
|
||||
val orderRepository = OrderRepository(apiService)
|
||||
val userRepository = UserRepository(apiService)
|
||||
RegisterViewModel(userRepository, orderRepository, requireContext())
|
||||
}
|
||||
}
|
||||
private var isEmailValid = false
|
||||
private var isPhoneValid = false
|
||||
|
||||
companion object {
|
||||
private const val TAG = "RegisterStep1Fragment"
|
||||
|
||||
fun newInstance() = RegisterStep1Fragment()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentRegisterStep1Binding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// Set step progress and description
|
||||
(activity as? RegisterActivity)?.let {
|
||||
it.findViewById<LinearProgressIndicator>(R.id.registration_progress)?.progress = 33
|
||||
it.findViewById<TextView>(R.id.tv_step_title)?.text = "Step 1: Account & Personal Info"
|
||||
it.findViewById<TextView>(R.id.tv_step_description)?.text =
|
||||
"Fill in your account and personal details to create your profile."
|
||||
}
|
||||
|
||||
setupFieldValidations()
|
||||
setupObservers()
|
||||
setupDatePicker()
|
||||
|
||||
binding.btnNext.setOnClickListener {
|
||||
validateAndProceed()
|
||||
}
|
||||
|
||||
binding.tvLoginAlt.setOnClickListener {
|
||||
startActivity(Intent(requireContext(), LoginActivity::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupDatePicker() {
|
||||
binding.etBirthDate.setOnClickListener {
|
||||
showDatePicker()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDatePicker() {
|
||||
val calendar = Calendar.getInstance()
|
||||
val year = calendar.get(Calendar.YEAR)
|
||||
val month = calendar.get(Calendar.MONTH)
|
||||
val day = calendar.get(Calendar.DAY_OF_MONTH)
|
||||
|
||||
DatePickerDialog(
|
||||
requireContext(),
|
||||
{ _, selectedYear, selectedMonth, selectedDay ->
|
||||
calendar.set(selectedYear, selectedMonth, selectedDay)
|
||||
val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||
binding.etBirthDate.setText(sdf.format(calendar.time))
|
||||
},
|
||||
year, month, day
|
||||
).show()
|
||||
}
|
||||
|
||||
private fun setupFieldValidations() {
|
||||
// Validate email when focus changes
|
||||
binding.etEmail.setOnFocusChangeListener { _, hasFocus ->
|
||||
if (!hasFocus) {
|
||||
val email = binding.etEmail.text.toString()
|
||||
if (email.isNotEmpty()) {
|
||||
validateEmail(email)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate phone when focus changes
|
||||
binding.etNumberPhone.setOnFocusChangeListener { _, hasFocus ->
|
||||
if (!hasFocus) {
|
||||
val phone = binding.etNumberPhone.text.toString()
|
||||
if (phone.isNotEmpty()) {
|
||||
validatePhone(phone)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateEmail(email: String) {
|
||||
val checkValueEmail = VerifRegisReq(
|
||||
fieldRegis = "email",
|
||||
valueRegis = email
|
||||
)
|
||||
registerViewModel.checkValueReg(checkValueEmail)
|
||||
}
|
||||
|
||||
private fun validatePhone(phone: String) {
|
||||
val checkValuePhone = VerifRegisReq(
|
||||
fieldRegis = "phone",
|
||||
valueRegis = phone
|
||||
)
|
||||
registerViewModel.checkValueReg(checkValuePhone)
|
||||
}
|
||||
|
||||
private fun setupObservers() {
|
||||
registerViewModel.checkValue.observe(viewLifecycleOwner) { result ->
|
||||
when (result) {
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Loading -> {
|
||||
// Show loading if needed
|
||||
}
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Success -> {
|
||||
val isValid = (result.data as? Boolean) ?: false
|
||||
when (val fieldType = registerViewModel.lastCheckedField) {
|
||||
"email" -> {
|
||||
isEmailValid = isValid
|
||||
if (!isValid) {
|
||||
Toast.makeText(requireContext(), "Email is already registered", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
"phone" -> {
|
||||
isPhoneValid = isValid
|
||||
if (!isValid) {
|
||||
Toast.makeText(requireContext(), "Phone number is already registered", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
|
||||
Toast.makeText(requireContext(), "Validation failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerViewModel.otpState.observe(viewLifecycleOwner) { result ->
|
||||
when (result) {
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Loading -> {
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
binding.btnNext.isEnabled = false
|
||||
}
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Success -> {
|
||||
binding.progressBar.visibility = View.GONE
|
||||
binding.btnNext.isEnabled = true
|
||||
|
||||
// Create user data with both account and personal info
|
||||
val userData = RegisterRequest(
|
||||
name = binding.etFullname.text.toString(),
|
||||
email = binding.etEmail.text.toString(),
|
||||
password = binding.etPassword.text.toString(),
|
||||
username = binding.etUsername.text.toString(),
|
||||
phone = binding.etNumberPhone.text.toString(),
|
||||
birthDate = binding.etBirthDate.text.toString(),
|
||||
otp = "" // Will be filled in step 2
|
||||
)
|
||||
|
||||
registerViewModel.updateUserData(userData)
|
||||
registerViewModel.setStep(2)
|
||||
(activity as? RegisterActivity)?.navigateToStep(2, userData)
|
||||
}
|
||||
is Result.Error -> {
|
||||
binding.progressBar.visibility = View.GONE
|
||||
binding.btnNext.isEnabled = true
|
||||
Toast.makeText(requireContext(), "OTP Request Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateAndProceed() {
|
||||
// Validate account information
|
||||
val email = binding.etEmail.text.toString()
|
||||
val password = binding.etPassword.text.toString()
|
||||
val confirmPassword = binding.etConfirmPassword.text.toString()
|
||||
val phone = binding.etNumberPhone.text.toString()
|
||||
val username = binding.etUsername.text.toString()
|
||||
|
||||
// Validate personal information
|
||||
val fullName = binding.etFullname.text.toString()
|
||||
val birthDate = binding.etBirthDate.text.toString()
|
||||
// val gender = binding.etGender.text.toString()
|
||||
|
||||
// Check if all fields are filled
|
||||
if (email.isEmpty() || password.isEmpty() || confirmPassword.isEmpty() || phone.isEmpty() ||
|
||||
username.isEmpty() || fullName.isEmpty() || birthDate.isEmpty()) {
|
||||
Toast.makeText(requireContext(), "Please fill all required fields", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
// Check if passwords match
|
||||
if (password != confirmPassword) {
|
||||
Toast.makeText(requireContext(), "Passwords do not match", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
// If both validations are already done and successful, request OTP
|
||||
if (isEmailValid && isPhoneValid) {
|
||||
requestOtp(email)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate email and phone
|
||||
validateEmail(email)
|
||||
validatePhone(phone)
|
||||
|
||||
// Only proceed if both are valid
|
||||
if (isEmailValid && isPhoneValid) {
|
||||
requestOtp(email)
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Please fix validation errors before proceeding", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestOtp(email: String) {
|
||||
|
||||
registerViewModel.requestOtp(email)
|
||||
|
||||
registerViewModel.message.observe(viewLifecycleOwner) { message ->
|
||||
Log.d(TAG, "Message from server: $message")
|
||||
// You can use the message here if needed, e.g., for showing in a specific UI element
|
||||
// or for storing for later use
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
@ -0,0 +1,291 @@
|
||||
package com.alya.ecommerce_serang.ui.auth.fragments
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.CountDownTimer
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import com.alya.ecommerce_serang.databinding.FragmentRegisterStep2Binding
|
||||
import com.alya.ecommerce_serang.ui.auth.RegisterActivity
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.alya.ecommerce_serang.utils.viewmodel.RegisterViewModel
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
|
||||
class RegisterStep2Fragment : Fragment() {
|
||||
private var _binding: FragmentRegisterStep2Binding? = null
|
||||
private val binding get() = _binding!!
|
||||
private lateinit var sessionManager: SessionManager
|
||||
|
||||
// In RegisterStep2Fragment AND RegisterStep3Fragment:
|
||||
private val registerViewModel: RegisterViewModel by activityViewModels {
|
||||
BaseViewModelFactory {
|
||||
val apiService = ApiConfig.getUnauthenticatedApiService()
|
||||
val orderRepository = OrderRepository(apiService)
|
||||
val userRepository = UserRepository(apiService)
|
||||
RegisterViewModel(userRepository, orderRepository, requireContext())
|
||||
}
|
||||
}
|
||||
private var countDownTimer: CountDownTimer? = null
|
||||
private var timeRemaining = 30 // 30 seconds cooldown for resend
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG = "RegisterStep2Fragment"
|
||||
fun newInstance(userData: RegisterRequest?) = RegisterStep2Fragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putParcelable("userData", userData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentRegisterStep2Binding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
sessionManager = SessionManager(requireContext())
|
||||
Log.d(TAG, "SessionManager initialized, token: ${sessionManager.getToken()}")
|
||||
|
||||
// Set step progress and description
|
||||
(activity as? RegisterActivity)?.let {
|
||||
it.findViewById<LinearProgressIndicator>(R.id.registration_progress)?.progress = 66
|
||||
it.findViewById<TextView>(R.id.tv_step_title)?.text = "Step 2: Verify Your Email"
|
||||
it.findViewById<TextView>(R.id.tv_step_description)?.text =
|
||||
"Enter the verification code sent to your email to continue."
|
||||
Log.d(TAG, "Step indicators updated to Step 2")
|
||||
}
|
||||
|
||||
|
||||
// Get the user data from arguments
|
||||
val userData = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
arguments?.getParcelable("userData", RegisterRequest::class.java)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
arguments?.getParcelable("userData") as? RegisterRequest
|
||||
}
|
||||
Log.d(TAG, "User data retrieved from arguments: ${userData?.email}, ${userData?.name}")
|
||||
|
||||
// Update the email sent message
|
||||
userData?.let {
|
||||
binding.tvEmailSent.text = "We've sent a verification code to ${it.email}"
|
||||
}
|
||||
|
||||
// Start the resend cooldown timer
|
||||
startResendCooldown()
|
||||
Log.d(TAG, "Resend cooldown timer started")
|
||||
|
||||
// Set up button listeners
|
||||
binding.btnVerify.setOnClickListener {
|
||||
verifyOtp(userData)
|
||||
}
|
||||
|
||||
binding.tvResendOtp.setOnClickListener {
|
||||
if (timeRemaining <= 0) {
|
||||
Log.d(TAG, "Resend OTP clicked, remaining time: $timeRemaining")
|
||||
resendOtp(userData?.email)
|
||||
} else {
|
||||
Log.d(TAG, "Resend OTP clicked but cooldown active, remaining time: $timeRemaining")
|
||||
}
|
||||
}
|
||||
|
||||
observeRegistrationState()
|
||||
observeLoginState()
|
||||
Log.d(TAG, "Registration and login state observers set up")
|
||||
}
|
||||
|
||||
private fun verifyOtp(userData: RegisterRequest?) {
|
||||
val otp = binding.etOtp.text.toString()
|
||||
Log.d(TAG, "verifyOtp called with OTP: $otp")
|
||||
|
||||
if (otp.isEmpty()) {
|
||||
Toast.makeText(requireContext(), "Please enter the verification code", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
// Update the user data with the OTP
|
||||
userData?.let {
|
||||
val updatedUserData = it.copy(otp = otp)
|
||||
Log.d(TAG, "Updating user data with OTP: $otp")
|
||||
registerViewModel.updateUserData(updatedUserData)
|
||||
|
||||
// For demo purposes, we're just proceeding to Step 3
|
||||
// In a real app, you would verify the OTP with the server first
|
||||
// registerViewModel.setStep(3)
|
||||
// (activity as? RegisterActivity)?.navigateToStep(3, updatedUserData)
|
||||
|
||||
registerViewModel.registerUser(updatedUserData)
|
||||
} ?: Log.e(TAG, "userData is null, cannot proceed with verification")
|
||||
}
|
||||
|
||||
private fun resendOtp(email: String?) {
|
||||
Log.d(TAG, "resendOtp called for email: $email")
|
||||
email?.let {
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
Log.d(TAG, "Requesting OTP for: $it")
|
||||
registerViewModel.requestOtp(it)
|
||||
|
||||
// Observe the OTP state
|
||||
registerViewModel.otpState.observe(viewLifecycleOwner) { result ->
|
||||
when (result) {
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Loading -> {
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
}
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Success -> {
|
||||
binding.progressBar.visibility = View.GONE
|
||||
Toast.makeText(requireContext(), "Verification code resent", Toast.LENGTH_SHORT).show()
|
||||
startResendCooldown()
|
||||
}
|
||||
is Result.Error -> {
|
||||
Log.e(TAG, "OTP request: Error - ${result.exception.message}")
|
||||
binding.progressBar.visibility = View.GONE
|
||||
Toast.makeText(requireContext(), "Failed to resend code: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, "OTP request: Unknown state")
|
||||
binding.progressBar.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
} ?: Log.e(TAG, "Cannot resend OTP: email is null")
|
||||
}
|
||||
|
||||
private fun startResendCooldown() {
|
||||
Log.d(TAG, "startResendCooldown called")
|
||||
timeRemaining = 30
|
||||
binding.tvResendOtp.isEnabled = false
|
||||
binding.tvResendOtp.setTextColor(ContextCompat.getColor(requireContext(), R.color.soft_gray))
|
||||
|
||||
countDownTimer?.cancel()
|
||||
countDownTimer = object : CountDownTimer(30000, 1000) {
|
||||
override fun onTick(millisUntilFinished: Long) {
|
||||
timeRemaining = (millisUntilFinished / 1000).toInt()
|
||||
binding.tvTimer.text = "Resend available in 00:${String.format("%02d", timeRemaining)}"
|
||||
if (timeRemaining % 5 == 0) {
|
||||
Log.d(TAG, "Cooldown remaining: $timeRemaining seconds")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
Log.d(TAG, "Cooldown finished, enabling resend button")
|
||||
binding.tvTimer.text = "You can now resend the code"
|
||||
binding.tvResendOtp.isEnabled = true
|
||||
binding.tvResendOtp.setTextColor(ContextCompat.getColor(requireContext(), R.color.blue1))
|
||||
timeRemaining = 0
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun observeRegistrationState() {
|
||||
registerViewModel.message.observe(viewLifecycleOwner) { message ->
|
||||
Log.d(TAG, "Message from server: $message")
|
||||
// You can use the message here if needed, e.g., for showing in a specific UI element
|
||||
// or for storing for later use
|
||||
}
|
||||
registerViewModel.registerState.observe(viewLifecycleOwner) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> {
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
binding.btnVerify.isEnabled = false
|
||||
}
|
||||
is Result.Success -> {
|
||||
Log.d(TAG, "Registration: Success - ${result.data}")
|
||||
// Don't hide progress bar or re-enable button yet
|
||||
// We'll wait for login to complete
|
||||
|
||||
// Don't show success toast yet - wait until address is added
|
||||
Log.d("RegisterStep2Fragment", "Registration successful, waiting for login")
|
||||
}
|
||||
is Result.Error -> {
|
||||
Log.e(TAG, "Registration: Error - ${result.exception.message}", result.exception)
|
||||
binding.progressBar.visibility = View.GONE
|
||||
binding.btnVerify.isEnabled = true
|
||||
|
||||
// Show error message
|
||||
Toast.makeText(requireContext(), "Registration Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, "Registration: Unknown state")
|
||||
binding.progressBar.visibility = View.GONE
|
||||
binding.btnVerify.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeLoginState() {
|
||||
registerViewModel.loginState.observe(viewLifecycleOwner) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> {
|
||||
// Keep showing progress
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
binding.btnVerify.isEnabled = false
|
||||
}
|
||||
is Result.Success -> {
|
||||
Log.d(TAG, "Login: Success - token received")
|
||||
binding.progressBar.visibility = View.GONE
|
||||
binding.btnVerify.isEnabled = true
|
||||
|
||||
// Save the token in fragment
|
||||
val accessToken = result.data.accessToken
|
||||
sessionManager.saveToken(accessToken)
|
||||
Log.d(TAG, "Token saved to SessionManager: $accessToken")
|
||||
|
||||
// Also save user ID if available in the login response
|
||||
// result.data.?.let { userId ->
|
||||
// sessionManager.saveUserId(userId)
|
||||
// }
|
||||
|
||||
Log.d(TAG, "Login successful, token saved: $accessToken")
|
||||
|
||||
// Proceed to Step 3
|
||||
Log.d(TAG, "Proceeding to Step 3 after successful login")
|
||||
(activity as? RegisterActivity)?.navigateToStep(3, null )
|
||||
}
|
||||
is Result.Error -> {
|
||||
Log.e(TAG, "Login: Error - ${result.exception.message}", result.exception)
|
||||
binding.progressBar.visibility = View.GONE
|
||||
binding.btnVerify.isEnabled = true
|
||||
|
||||
// Show error message but continue to Step 3 anyway
|
||||
Log.e(TAG, "Login failed but proceeding to Step 3", result.exception)
|
||||
Toast.makeText(requireContext(), "Note: Auto-login failed, but registration was successful", Toast.LENGTH_SHORT).show()
|
||||
|
||||
// Proceed to Step 3
|
||||
(activity as? RegisterActivity)?.navigateToStep(3, null)
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, "Login: Unknown state")
|
||||
binding.progressBar.visibility = View.GONE
|
||||
binding.btnVerify.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
countDownTimer?.cancel()
|
||||
_binding = null
|
||||
}
|
||||
}
|
@ -0,0 +1,360 @@
|
||||
package com.alya.ecommerce_serang.ui.auth.fragments
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.CreateAddressRequest
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import com.alya.ecommerce_serang.databinding.FragmentRegisterStep3Binding
|
||||
import com.alya.ecommerce_serang.ui.auth.LoginActivity
|
||||
import com.alya.ecommerce_serang.ui.auth.RegisterActivity
|
||||
import com.alya.ecommerce_serang.ui.order.address.CityAdapter
|
||||
import com.alya.ecommerce_serang.ui.order.address.ProvinceAdapter
|
||||
import com.alya.ecommerce_serang.ui.order.address.ViewState
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.alya.ecommerce_serang.utils.viewmodel.RegisterViewModel
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
|
||||
class RegisterStep3Fragment : Fragment() {
|
||||
private var _binding: FragmentRegisterStep3Binding? = null
|
||||
private val binding get() = _binding!!
|
||||
private lateinit var sessionManager: SessionManager
|
||||
|
||||
private val defaultLatitude = -6.200000
|
||||
private val defaultLongitude = 106.816666
|
||||
|
||||
// In RegisterStep2Fragment AND RegisterStep3Fragment:
|
||||
private val registerViewModel: RegisterViewModel by activityViewModels {
|
||||
BaseViewModelFactory {
|
||||
val apiService = ApiConfig.getUnauthenticatedApiService()
|
||||
val orderRepository = OrderRepository(apiService)
|
||||
val userRepository = UserRepository(apiService)
|
||||
RegisterViewModel(userRepository, orderRepository, requireContext())
|
||||
}
|
||||
}
|
||||
// For province and city selection
|
||||
private val provinceAdapter by lazy { ProvinceAdapter(requireContext()) }
|
||||
private val cityAdapter by lazy { CityAdapter(requireContext()) }
|
||||
|
||||
companion object {
|
||||
private const val TAG = "RegisterStep3Fragment"
|
||||
|
||||
fun newInstance() = RegisterStep3Fragment()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentRegisterStep3Binding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
sessionManager = SessionManager(requireContext())
|
||||
Log.d(TAG, "SessionManager initialized, token: ${sessionManager.getToken()}")
|
||||
|
||||
// Set step progress and description
|
||||
(activity as? RegisterActivity)?.let {
|
||||
it.findViewById<LinearProgressIndicator>(R.id.registration_progress)?.progress = 33
|
||||
it.findViewById<TextView>(R.id.tv_step_title)?.text = "Step 1: Account & Personal Info"
|
||||
it.findViewById<TextView>(R.id.tv_step_description)?.text =
|
||||
"Fill in your account and personal details to create your profile."
|
||||
Log.d(TAG, "Step indicators updated to Step 1")
|
||||
}
|
||||
|
||||
// Get registered user data
|
||||
val user = registerViewModel.registeredUser.value
|
||||
Log.d(TAG, "Retrieved user data: ${user?.name}, ID: ${user?.id}")
|
||||
|
||||
// Auto-fill recipient name and phone if available
|
||||
user?.let {
|
||||
binding.etNamaPenerima.setText(it.name)
|
||||
binding.etNomorHp.setText(it.phone)
|
||||
Log.d(TAG, "Auto-filled name: ${it.name}, phone: ${it.phone}")
|
||||
}
|
||||
|
||||
// Set up province and city dropdowns
|
||||
setupAutoComplete()
|
||||
|
||||
// Set up button listeners
|
||||
binding.btnPrevious.setOnClickListener {
|
||||
// Go back to the previous step
|
||||
parentFragmentManager.popBackStack()
|
||||
}
|
||||
|
||||
binding.btnRegister.setOnClickListener {
|
||||
submitAddress()
|
||||
}
|
||||
|
||||
// If user skips address entry
|
||||
// binding.btnSkip.setOnClickListener {
|
||||
// showRegistrationSuccess()
|
||||
// }
|
||||
|
||||
// Observe address submission state
|
||||
observeAddressSubmissionState()
|
||||
|
||||
// Load provinces
|
||||
Log.d(TAG, "Requesting provinces data")
|
||||
registerViewModel.getProvinces()
|
||||
setupProvinceObserver()
|
||||
setupCityObserver()
|
||||
}
|
||||
|
||||
private fun setupAutoComplete() {
|
||||
// Same implementation as before
|
||||
binding.autoCompleteProvinsi.setAdapter(provinceAdapter)
|
||||
binding.autoCompleteKabupaten.setAdapter(cityAdapter)
|
||||
|
||||
binding.autoCompleteProvinsi.setOnClickListener {
|
||||
binding.autoCompleteProvinsi.showDropDown()
|
||||
}
|
||||
|
||||
binding.autoCompleteKabupaten.setOnClickListener {
|
||||
if (cityAdapter.count > 0) {
|
||||
Log.d(TAG, "City dropdown clicked, showing ${cityAdapter.count} cities")
|
||||
binding.autoCompleteKabupaten.showDropDown()
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Pilih provinsi terlebih dahulu", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
binding.autoCompleteProvinsi.setOnItemClickListener { _, _, position, _ ->
|
||||
val provinceId = provinceAdapter.getProvinceId(position)
|
||||
Log.d(TAG, "Province selected at position $position, ID: $provinceId")
|
||||
|
||||
provinceId?.let { id ->
|
||||
registerViewModel.selectedProvinceId = id
|
||||
Log.d(TAG, "Requesting cities for province ID: $id")
|
||||
registerViewModel.getCities(id)
|
||||
binding.autoCompleteKabupaten.text.clear()
|
||||
}
|
||||
}
|
||||
|
||||
binding.autoCompleteKabupaten.setOnItemClickListener { _, _, position, _ ->
|
||||
val cityId = cityAdapter.getCityId(position)
|
||||
Log.d(TAG, "City selected at position $position, ID: $cityId")
|
||||
|
||||
cityId?.let { id ->
|
||||
Log.d(TAG, "Selected city ID set to: $id")
|
||||
registerViewModel.selectedCityId = id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupProvinceObserver() {
|
||||
// Same implementation as before
|
||||
registerViewModel.provincesState.observe(viewLifecycleOwner) { state ->
|
||||
when (state) {
|
||||
is ViewState.Loading -> {
|
||||
binding.progressBarProvinsi.visibility = View.VISIBLE
|
||||
}
|
||||
is ViewState.Success -> {
|
||||
Log.d(TAG, "Provinces: Success - received ${state.data.size} provinces")
|
||||
binding.progressBarProvinsi.visibility = View.GONE
|
||||
if (state.data.isNotEmpty()) {
|
||||
provinceAdapter.updateData(state.data)
|
||||
} else {
|
||||
showError("No provinces available")
|
||||
}
|
||||
}
|
||||
is ViewState.Error -> {
|
||||
Log.e(TAG, "Provinces: Error - ${state.message}")
|
||||
binding.progressBarProvinsi.visibility = View.GONE
|
||||
showError("Failed to load provinces: ${state.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupCityObserver() {
|
||||
// Same implementation as before
|
||||
registerViewModel.citiesState.observe(viewLifecycleOwner) { state ->
|
||||
when (state) {
|
||||
is ViewState.Loading -> {
|
||||
binding.progressBarKabupaten.visibility = View.VISIBLE
|
||||
}
|
||||
is ViewState.Success -> {
|
||||
Log.d(TAG, "Cities: Success - received ${state.data.size} cities")
|
||||
binding.progressBarKabupaten.visibility = View.GONE
|
||||
cityAdapter.updateData(state.data)
|
||||
Log.d(TAG, "Updated city adapter with ${state.data.size} items")
|
||||
}
|
||||
is ViewState.Error -> {
|
||||
Log.e(TAG, "Cities: Error - ${state.message}")
|
||||
binding.progressBarKabupaten.visibility = View.GONE
|
||||
showError("Failed to load cities: ${state.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun submitAddress() {
|
||||
Log.d(TAG, "submitAddress called")
|
||||
if (!validateAddressForm()) {
|
||||
Log.w(TAG, "Address form validation failed")
|
||||
return
|
||||
}
|
||||
|
||||
val user = registerViewModel.registeredUser.value
|
||||
if (user == null) {
|
||||
Log.e(TAG, "User data not available")
|
||||
showError("User data not available. Please try again.")
|
||||
return
|
||||
}
|
||||
|
||||
val userId = user.id
|
||||
Log.d(TAG, "Using user ID: $userId")
|
||||
|
||||
val street = binding.etDetailAlamat.text.toString().trim()
|
||||
val subDistrict = binding.etKecamatan.text.toString().trim()
|
||||
val postalCode = binding.etKodePos.text.toString().trim()
|
||||
val recipient = binding.etNamaPenerima.text.toString().trim()
|
||||
val phone = binding.etNomorHp.text.toString().trim()
|
||||
|
||||
val provinceId = registerViewModel.selectedProvinceId?.toInt() ?: 0
|
||||
val cityId = registerViewModel.selectedCityId?.toInt() ?: 0
|
||||
|
||||
Log.d(TAG, "Address data - Street: $street, SubDistrict: $subDistrict, PostalCode: $postalCode")
|
||||
Log.d(TAG, "Address data - Recipient: $recipient, Phone: $phone")
|
||||
Log.d(TAG, "Address data - ProvinceId: $provinceId, CityId: $cityId")
|
||||
Log.d(TAG, "Address data - Lat: $defaultLatitude, Long: $defaultLongitude")
|
||||
|
||||
// Create address request
|
||||
val addressRequest = CreateAddressRequest(
|
||||
lat = defaultLatitude,
|
||||
long = defaultLongitude,
|
||||
street = street,
|
||||
subDistrict = subDistrict,
|
||||
cityId = cityId,
|
||||
provId = provinceId,
|
||||
postCode = postalCode,
|
||||
detailAddress = street,
|
||||
userId = userId,
|
||||
recipient = recipient,
|
||||
phone = phone,
|
||||
isStoreLocation = false
|
||||
)
|
||||
|
||||
Log.d(TAG, "Address request created: $addressRequest")
|
||||
|
||||
// Show loading
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
binding.btnRegister.isEnabled = false
|
||||
// binding.btnSkip.isEnabled = false
|
||||
|
||||
// Submit address
|
||||
registerViewModel.addAddress(addressRequest)
|
||||
}
|
||||
|
||||
private fun validateAddressForm(): Boolean {
|
||||
val street = binding.etDetailAlamat.text.toString().trim()
|
||||
val subDistrict = binding.etKecamatan.text.toString().trim()
|
||||
val postalCode = binding.etKodePos.text.toString().trim()
|
||||
val recipient = binding.etNamaPenerima.text.toString().trim()
|
||||
val phone = binding.etNomorHp.text.toString().trim()
|
||||
|
||||
val provinceId = registerViewModel.selectedProvinceId
|
||||
val cityId = registerViewModel.selectedCityId
|
||||
|
||||
Log.d(TAG, "Validating - Street: $street, SubDistrict: $subDistrict, PostalCode: $postalCode")
|
||||
Log.d(TAG, "Validating - Recipient: $recipient, Phone: $phone")
|
||||
Log.d(TAG, "Validating - ProvinceId: $provinceId, CityId: $cityId")
|
||||
|
||||
|
||||
// Validate required fields
|
||||
if (street.isBlank()) {
|
||||
binding.etDetailAlamat.error = "Alamat tidak boleh kosong"
|
||||
binding.etDetailAlamat.requestFocus()
|
||||
return false
|
||||
}
|
||||
|
||||
if (recipient.isBlank()) {
|
||||
binding.etNamaPenerima.error = "Nama penerima tidak boleh kosong"
|
||||
binding.etNamaPenerima.requestFocus()
|
||||
return false
|
||||
}
|
||||
|
||||
if (phone.isBlank()) {
|
||||
binding.etNomorHp.error = "Nomor HP tidak boleh kosong"
|
||||
binding.etNomorHp.requestFocus()
|
||||
return false
|
||||
}
|
||||
|
||||
if (provinceId == null) {
|
||||
showError("Pilih provinsi terlebih dahulu")
|
||||
binding.autoCompleteProvinsi.requestFocus()
|
||||
return false
|
||||
}
|
||||
|
||||
if (cityId == null) {
|
||||
showError("Pilih kota/kabupaten terlebih dahulu")
|
||||
binding.autoCompleteKabupaten.requestFocus()
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun observeAddressSubmissionState() {
|
||||
registerViewModel.addressSubmissionState.observe(viewLifecycleOwner) { state ->
|
||||
when (state) {
|
||||
is ViewState.Loading -> {
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
binding.btnRegister.isEnabled = false
|
||||
// binding.btnSkip.isEnabled = false
|
||||
}
|
||||
is ViewState.Success -> {
|
||||
Log.d(TAG, "Address submission: Success - ${state.data}")
|
||||
binding.progressBar.visibility = View.GONE
|
||||
showRegistrationSuccess()
|
||||
}
|
||||
is ViewState.Error -> {
|
||||
Log.e(TAG, "Address submission: Error - ${state.message}")
|
||||
binding.progressBar.visibility = View.GONE
|
||||
binding.btnRegister.isEnabled = true
|
||||
// binding.btnSkip.isEnabled = true
|
||||
showError("Failed to add address: ${state.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showRegistrationSuccess() {
|
||||
// Now we can show the success message for the overall registration process
|
||||
Toast.makeText(requireContext(), "Registration completed successfully!", Toast.LENGTH_LONG).show()
|
||||
|
||||
// Navigate to login screen
|
||||
startActivity(Intent(requireContext(), LoginActivity::class.java))
|
||||
Log.d(TAG, "Navigating to LoginActivity")
|
||||
activity?.finish()
|
||||
}
|
||||
|
||||
private fun showError(message: String) {
|
||||
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
//
|
||||
// // Data classes for province and city
|
||||
// data class Province(val id: String, val name: String)
|
||||
// data class City(val id: String, val name: String)
|
||||
}
|
@ -1,20 +1,49 @@
|
||||
package com.alya.ecommerce_serang.utils.viewmodel
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.alya.ecommerce_serang.data.api.dto.CreateAddressRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.VerifRegisReq
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.LoginResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.OtpResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.RegisterResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.User
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.VerifRegisterResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CitiesItem
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.ProvincesItem
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import com.alya.ecommerce_serang.ui.order.address.ViewState
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class RegisterViewModel(private val repository: UserRepository) : ViewModel() {
|
||||
class RegisterViewModel(private val repository: UserRepository, private val orderRepo: OrderRepository, private val context: Context) : ViewModel() {
|
||||
|
||||
private val _loginState = MutableLiveData<Result<LoginResponse>>()
|
||||
val loginState: LiveData<Result<LoginResponse>> get() = _loginState
|
||||
|
||||
// To track if user is authenticated
|
||||
private val _isAuthenticated = MutableLiveData<Boolean>(false)
|
||||
val isAuthenticated: LiveData<Boolean> = _isAuthenticated
|
||||
|
||||
private var _lastCheckedField = MutableLiveData<String>()
|
||||
val lastCheckedField: String
|
||||
get() = _lastCheckedField.value ?: ""
|
||||
|
||||
private val _userData = MutableLiveData<RegisterRequest>()
|
||||
val userData: LiveData<RegisterRequest> = _userData
|
||||
|
||||
// Current step in the registration process
|
||||
private val _currentStep = MutableLiveData<Int>(1)
|
||||
val currentStep: LiveData<Int> = _currentStep
|
||||
// MutableLiveData for handling register state (Loading, Success, or Error)
|
||||
private val _registerState = MutableLiveData<Result<String>>()
|
||||
val registerState: LiveData<Result<String>> = _registerState
|
||||
@ -30,19 +59,55 @@ class RegisterViewModel(private val repository: UserRepository) : ViewModel() {
|
||||
private val _message = MutableLiveData<String>()
|
||||
val message: LiveData<String> = _message
|
||||
|
||||
private val _registeredUser = MutableLiveData<User>()
|
||||
val registeredUser: LiveData<User> = _registeredUser
|
||||
|
||||
// For address data
|
||||
var selectedProvinceId: Int? = null
|
||||
var selectedCityId: Int? = null
|
||||
|
||||
// For provinces and cities
|
||||
private val _provincesState = MutableLiveData<ViewState<List<ProvincesItem>>>()
|
||||
val provincesState: LiveData<ViewState<List<ProvincesItem>>> = _provincesState
|
||||
|
||||
private val _citiesState = MutableLiveData<ViewState<List<CitiesItem>>>()
|
||||
val citiesState: LiveData<ViewState<List<CitiesItem>>> = _citiesState
|
||||
|
||||
// For address submission
|
||||
private val _addressSubmissionState = MutableLiveData<ViewState<String>>()
|
||||
val addressSubmissionState: LiveData<ViewState<String>> = _addressSubmissionState
|
||||
|
||||
private val sessionManager by lazy { SessionManager(context) }
|
||||
|
||||
// For authenticated API calls
|
||||
private fun getAuthenticatedApiService(): ApiService {
|
||||
return ApiConfig.getApiService(sessionManager)
|
||||
}
|
||||
|
||||
fun updateUserData(updatedData: RegisterRequest) {
|
||||
_userData.value = updatedData
|
||||
}
|
||||
|
||||
// Set current step
|
||||
fun setStep(step: Int) {
|
||||
_currentStep.value = step
|
||||
}
|
||||
/**
|
||||
* Function to request OTP by sending an email to the API.
|
||||
* - It sets the OTP state to `Loading` before calling the repository.
|
||||
* - If successful, it updates `_message` with the response message and signals success.
|
||||
* - If an error occurs, it updates `_otpState` with `Result.Error` and logs the failure.
|
||||
*/
|
||||
|
||||
fun requestOtp(email: String) {
|
||||
viewModelScope.launch {
|
||||
_otpState.value = Result.Loading // Indicating API call in progress
|
||||
|
||||
try {
|
||||
// Call the repository function to request OTP
|
||||
val response: OtpResponse = repository.requestOtpRep(email)
|
||||
val authenticatedApiService = getAuthenticatedApiService()
|
||||
val authenticatedOrderRepo = UserRepository(authenticatedApiService)
|
||||
val response: OtpResponse = authenticatedOrderRepo.requestOtpRep(email)
|
||||
|
||||
// Log and store success message
|
||||
Log.d("RegisterViewModel", "OTP Response: ${response.message}")
|
||||
@ -75,16 +140,43 @@ class RegisterViewModel(private val repository: UserRepository) : ViewModel() {
|
||||
|
||||
try {
|
||||
// Call repository function to register the user
|
||||
val message = repository.registerUser(request)
|
||||
val response: RegisterResponse = repository.registerUser(request)
|
||||
|
||||
// Store and display success message
|
||||
_message.value = message
|
||||
_registerState.value = Result.Success(message) // Store success result
|
||||
Log.d(TAG, "Registration API call successful")
|
||||
Log.d(TAG, "Response message: ${response.message}")
|
||||
Log.d(TAG, "User ID received: ${response.user.id}")
|
||||
Log.d(TAG, "User details - Name: ${response.user.name}, Email: ${response.user.email}, Username: ${response.user.username}")
|
||||
|
||||
// Store the user data
|
||||
_registeredUser.value = response.user
|
||||
Log.d(TAG, "User data stored in ViewModel")
|
||||
|
||||
// Store success message
|
||||
_message.value = response.message
|
||||
Log.d(TAG, "Success message stored: ${response.message}")
|
||||
|
||||
_registerState.value = Result.Success(response.message)
|
||||
|
||||
// Automatically login after successful registration
|
||||
request.email?.let { email ->
|
||||
|
||||
request.password?.let { password ->
|
||||
Log.d(TAG, "Attempting auto-login with email: $email")
|
||||
|
||||
login(email, password)
|
||||
}
|
||||
}
|
||||
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Registration failed with exception: ${exception.javaClass.simpleName}", exception)
|
||||
Log.e(TAG, "Exception message: ${exception.message}")
|
||||
Log.e(TAG, "Exception cause: ${exception.cause}")
|
||||
// Handle any errors and update state
|
||||
_registerState.value = Result.Error(exception)
|
||||
|
||||
_message.value = exception.localizedMessage ?: "Registration failed"
|
||||
Log.d(TAG, "Error message stored: ${exception.localizedMessage ?: "Registration failed"}")
|
||||
|
||||
|
||||
// Log the error for debugging
|
||||
Log.e("RegisterViewModel", "User registration failed", exception)
|
||||
@ -92,7 +184,26 @@ class RegisterViewModel(private val repository: UserRepository) : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun login(email: String, password: String) {
|
||||
viewModelScope.launch {
|
||||
_loginState.value = Result.Loading
|
||||
try {
|
||||
val result = repository.login(email, password)
|
||||
_loginState.value = result
|
||||
|
||||
// Update authentication status if login was successful
|
||||
if (result is Result.Success) {
|
||||
_isAuthenticated.value = true
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
_loginState.value = Result.Error(exception)
|
||||
Log.e("RegisterViewModel", "Login failed", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun checkValueReg(request: VerifRegisReq){
|
||||
_lastCheckedField.value = request.fieldRegis
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
// Call the repository function to request OTP
|
||||
@ -111,4 +222,97 @@ class RegisterViewModel(private val repository: UserRepository) : ViewModel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getProvinces() {
|
||||
_provincesState.value = ViewState.Loading
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val result = repository.getListProvinces()
|
||||
if (result?.provinces != null) {
|
||||
_provincesState.postValue(ViewState.Success(result.provinces))
|
||||
Log.d(TAG, "Provinces loaded: ${result.provinces.size}")
|
||||
} else {
|
||||
_provincesState.postValue(ViewState.Error("Failed to load provinces"))
|
||||
Log.e(TAG, "Province result was null or empty")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
_provincesState.postValue(ViewState.Error(e.message ?: "Error loading provinces"))
|
||||
Log.e(TAG, "Error fetching provinces", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getCities(provinceId: Int) {
|
||||
_citiesState.value = ViewState.Loading
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
|
||||
selectedProvinceId = provinceId
|
||||
val result = repository.getListCities(provinceId)
|
||||
result?.let {
|
||||
_citiesState.postValue(ViewState.Success(it.cities))
|
||||
Log.d(TAG, "Cities loaded for province $provinceId: ${it.cities.size}")
|
||||
} ?: run {
|
||||
_citiesState.postValue(ViewState.Error("Failed to load cities"))
|
||||
Log.e(TAG, "City result was null for province $provinceId")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
_citiesState.postValue(ViewState.Error(e.message ?: "Error loading cities"))
|
||||
Log.e(TAG, "Error fetching cities for province $provinceId", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setSelectedProvinceId(id: Int) {
|
||||
selectedProvinceId = id
|
||||
}
|
||||
|
||||
fun setSelectedCityId(id: Int) {
|
||||
selectedCityId = id
|
||||
}
|
||||
|
||||
fun addAddress(request: CreateAddressRequest) {
|
||||
Log.d(TAG, "Starting address submission process")
|
||||
_addressSubmissionState.value = ViewState.Loading
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val authenticatedApiService = getAuthenticatedApiService()
|
||||
val authenticatedOrderRepo = OrderRepository(authenticatedApiService)
|
||||
Log.d(TAG, "Calling repository.addAddress with request: $request")
|
||||
val result = authenticatedOrderRepo.addAddress(request)
|
||||
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
val message = result.data.message
|
||||
Log.d(TAG, "Address added successfully: $message")
|
||||
_addressSubmissionState.postValue(ViewState.Success(message))
|
||||
}
|
||||
is Result.Error -> {
|
||||
val errorMsg = result.exception.message ?: "Unknown error"
|
||||
Log.e(TAG, "Error from repository: $errorMsg", result.exception)
|
||||
_addressSubmissionState.postValue(ViewState.Error(errorMsg))
|
||||
}
|
||||
is Result.Loading -> {
|
||||
Log.d(TAG, "Repository returned Loading state")
|
||||
// We already set Loading at the beginning
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Exception occurred during address submission", e)
|
||||
val errorMessage = e.message ?: "Unknown error occurred"
|
||||
Log.e(TAG, "Error message: $errorMessage")
|
||||
|
||||
// Log the exception stack trace
|
||||
e.printStackTrace()
|
||||
|
||||
_addressSubmissionState.postValue(ViewState.Error(errorMessage))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "RegisterViewModel"
|
||||
}
|
||||
|
||||
//require auth
|
||||
}
|
@ -1,256 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:theme="@style/Theme.Ecommerce_serang"
|
||||
tools:context=".ui.auth.RegisterActivity">
|
||||
|
||||
<LinearLayout
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/registration_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:padding="16dp">
|
||||
android:layout_marginTop="16dp"
|
||||
app:trackThickness="8dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Buat Akun"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:textAlignment="center"
|
||||
android:layout_marginBottom="24dp"/>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="18sp"
|
||||
android:text="@string/email"/>
|
||||
<!-- Step Title -->
|
||||
<TextView
|
||||
android:id="@+id/tv_step_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
<!-- Step Description -->
|
||||
<TextView
|
||||
android:id="@+id/tv_step_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_email"
|
||||
android:inputType="textEmailAddress"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="18sp"
|
||||
android:text="@string/username"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_username"
|
||||
android:inputType="text"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="18sp"
|
||||
android:text="@string/full_name"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_fullname"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_fullname"
|
||||
android:inputType="text"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="18sp"
|
||||
android:text="@string/password"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
app:passwordToggleEnabled="true">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_password"
|
||||
android:inputType="textPassword"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="18sp"
|
||||
android:text="@string/confirm_password"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
app:passwordToggleEnabled="true">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_confirm_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_confirmation_password"
|
||||
android:inputType="textPassword"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="18sp"
|
||||
android:text="@string/birth_date"/>
|
||||
<!-- Fragment Container -->
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/SharpedBorderStyleOutline"
|
||||
app:endIconMode="custom"
|
||||
app:endIconDrawable="@drawable/outline_calendar_today_24">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_birth_date"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Pilih tanggal"
|
||||
android:focusable="false"
|
||||
android:clickable="true"
|
||||
android:minHeight="50dp"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="18sp"
|
||||
android:text="@string/gender"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/et_gender"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_gender"
|
||||
android:inputType="textFilter"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="18sp"
|
||||
android:text="@string/number_phone"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_number_phone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_number_phone"
|
||||
android:inputType="phone"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_signup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/signup"
|
||||
android:layout_marginTop="16dp"
|
||||
app:cornerRadius="8dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/no_account"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_login_alt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/login"
|
||||
android:textColor="@color/blue1"
|
||||
android:textStyle="bold"/>
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBarOtp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"/>
|
||||
|
||||
<!-- ProgressBar for Registration -->
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBarRegister"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"/>
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
245
app/src/main/res/layout/fragment_register_step1.xml
Normal file
245
app/src/main/res/layout/fragment_register_step1.xml
Normal file
@ -0,0 +1,245 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- Account Information Section -->
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="14sp"
|
||||
android:text="@string/email"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_email"
|
||||
android:inputType="textEmailAddress"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Username Field -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="14sp"
|
||||
android:text="@string/username"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_username"
|
||||
android:inputType="text"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Password Field -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="14sp"
|
||||
android:text="@string/password"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
app:passwordToggleEnabled="true">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_password"
|
||||
android:inputType="textPassword"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Confirm Password Field -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="14sp"
|
||||
android:text="@string/confirm_password"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
app:passwordToggleEnabled="true">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_confirm_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_confirmation_password"
|
||||
android:inputType="textPassword"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Phone Number Field -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="14sp"
|
||||
android:text="@string/number_phone"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_number_phone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_number_phone"
|
||||
android:inputType="phone"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Full Name Field -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="14sp"
|
||||
android:text="@string/full_name"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_fullname"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_fullname"
|
||||
android:inputType="text"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Birth Date Field -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="14sp"
|
||||
android:text="@string/birth_date"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
style="@style/SharpedBorderStyleOutline"
|
||||
app:endIconMode="custom"
|
||||
app:endIconDrawable="@drawable/outline_calendar_today_24">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_birth_date"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Pilih tanggal"
|
||||
android:focusable="false"
|
||||
android:clickable="true"
|
||||
android:minHeight="50dp"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- <!– Gender Field –>-->
|
||||
<!-- <TextView-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_marginTop="4dp"-->
|
||||
<!-- android:fontFamily="@font/dmsans_medium"-->
|
||||
<!-- android:textSize="14sp"-->
|
||||
<!-- android:text="@string/gender"/>-->
|
||||
|
||||
<!-- <com.google.android.material.textfield.TextInputLayout-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_marginBottom="12dp"-->
|
||||
<!-- style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu">-->
|
||||
|
||||
<!-- <AutoCompleteTextView-->
|
||||
<!-- android:id="@+id/et_gender"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:hint="@string/hint_gender"-->
|
||||
<!-- android:inputType="textFilter"/>-->
|
||||
<!-- </com.google.android.material.textfield.TextInputLayout>-->
|
||||
|
||||
<!-- Next Button -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_next"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Next"
|
||||
android:layout_marginTop="16dp"
|
||||
app:cornerRadius="8dp"/>
|
||||
|
||||
<!-- Login Alternative -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/no_account"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_login_alt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/login"
|
||||
android:textColor="@color/blue1"
|
||||
android:textStyle="bold"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
108
app/src/main/res/layout/fragment_register_step2.xml
Normal file
108
app/src/main/res/layout/fragment_register_step2.xml
Normal file
@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- OTP Verification Image -->
|
||||
<ImageView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="16dp"
|
||||
android:src="@drawable/outline_notifications_24"
|
||||
android:contentDescription="OTP Verification" />
|
||||
|
||||
<!-- OTP Input Field -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="18sp"
|
||||
android:text="Enter Verification Code"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_email_sent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="We've sent a verification code to your email"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<!-- OTP Input Layout -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_otp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Enter OTP"
|
||||
android:inputType="number"
|
||||
android:textAlignment="center"
|
||||
android:maxLength="6" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Verify Button -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_verify"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Verify"
|
||||
app:cornerRadius="8dp" />
|
||||
|
||||
<!-- Resend OTP -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Didn't receive the code? " />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_resend_otp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Resend"
|
||||
android:textColor="@color/blue1"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Timer for resend cooldown -->
|
||||
<TextView
|
||||
android:id="@+id/tv_timer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Resend available in 00:30"
|
||||
android:textAlignment="center"
|
||||
android:visibility="visible" />
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="16dp"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
229
app/src/main/res/layout/fragment_register_step3.xml
Normal file
229
app/src/main/res/layout/fragment_register_step3.xml
Normal file
@ -0,0 +1,229 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/btn_register"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Nama Penerima"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_nama_penerima"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/edit_text_background"
|
||||
android:hint="Isi nama penerima"
|
||||
android:inputType="textPersonName"
|
||||
android:padding="12dp"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Nomor Hp"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_nomor_hp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/edit_text_background"
|
||||
android:hint="Isi nomor handphone aktif"
|
||||
android:inputType="phone"
|
||||
android:padding="12dp"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Detail Alamat"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_detail_alamat"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/edit_text_background"
|
||||
android:gravity="top"
|
||||
android:hint="Isi detail alamat (nomor rumah, lantai, dll)"
|
||||
android:inputType="textMultiLine"
|
||||
android:lines="3"
|
||||
android:padding="12dp"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<!-- Provinsi -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Provinsi"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:hint="Pilih Provinsi"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/autoCompleteProvinsi"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none"
|
||||
android:focusable="false"
|
||||
android:clickable="true"
|
||||
android:padding="12dp"
|
||||
android:textSize="14sp" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar_provinsi"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="8dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<!-- Kabupaten / Kota -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Kabupaten / Kota"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:hint="Pilih Kabupaten / Kota"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/autoCompleteKabupaten"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none"
|
||||
android:focusable="false"
|
||||
android:clickable="true"
|
||||
android:padding="12dp"
|
||||
android:textSize="14sp" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar_kabupaten"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="8dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<!-- Kecamatan / Desa -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Kecamatan / Desa"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:hint="Isi Kecamatan / Desa"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_kecamatan"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:textSize="14sp"
|
||||
android:inputType="textCapWords" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Kode Pos -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Kode Pos"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_kode_pos"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/edit_text_background"
|
||||
android:hint="Isi kode pos"
|
||||
android:inputType="number"
|
||||
android:padding="12dp"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<!-- Navigation Button (Previous) -->
|
||||
<Button
|
||||
android:id="@+id/btn_previous"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:background="@drawable/bg_button_outline"
|
||||
android:text="Previous"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/blue1"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
<!-- Register Button -->
|
||||
<Button
|
||||
android:id="@+id/btn_register"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_margin="16dp"
|
||||
android:background="@drawable/button_address_background"
|
||||
android:text="@string/signup"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Reference in New Issue
Block a user