feat: implement register feature
This commit is contained in:
parent
90df005a3f
commit
fc9c9614ba
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.syaroful.agrilinkvocpro.data.model
|
||||||
|
|
||||||
|
data class RegisterResponse(
|
||||||
|
val message: String?,
|
||||||
|
val success: Boolean?
|
||||||
|
)
|
||||||
|
|
@ -5,6 +5,7 @@ import com.syaroful.agrilinkvocpro.presentation.screen.home.DynamicModuleViewMod
|
||||||
import com.syaroful.agrilinkvocpro.presentation.screen.home.HomeViewModel
|
import com.syaroful.agrilinkvocpro.presentation.screen.home.HomeViewModel
|
||||||
import com.syaroful.agrilinkvocpro.presentation.screen.login.LoginViewModel
|
import com.syaroful.agrilinkvocpro.presentation.screen.login.LoginViewModel
|
||||||
import com.syaroful.agrilinkvocpro.presentation.screen.profile.ProfileViewModel
|
import com.syaroful.agrilinkvocpro.presentation.screen.profile.ProfileViewModel
|
||||||
|
import com.syaroful.agrilinkvocpro.presentation.screen.register.RegisterViewModel
|
||||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
|
@ -14,4 +15,5 @@ val viewModelModule = module {
|
||||||
viewModel { ProfileViewModel(get()) }
|
viewModel { ProfileViewModel(get()) }
|
||||||
viewModel { HomeViewModel(get(), get()) }
|
viewModel { HomeViewModel(get(), get()) }
|
||||||
viewModel { DetailViewModel(get(), get()) }
|
viewModel { DetailViewModel(get(), get()) }
|
||||||
|
viewModel { RegisterViewModel(get()) }
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +55,7 @@ fun SetupNavigation(
|
||||||
}
|
}
|
||||||
composable("register") {
|
composable("register") {
|
||||||
RegisterScreen(
|
RegisterScreen(
|
||||||
onLoginSuccess = {
|
onRegisterSuccess = {
|
||||||
navController.navigate("login") {
|
navController.navigate("login") {
|
||||||
popUpTo("login") { inclusive = true }
|
popUpTo("login") { inclusive = true }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.syaroful.agrilinkvocpro.presentation.screen.login
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.syaroful.agrilinkvocpro.core.utils.ResultState
|
import com.syaroful.agrilinkvocpro.core.utils.ResultState
|
||||||
|
import com.syaroful.agrilinkvocpro.core.utils.extention.mapToUserFriendlyError
|
||||||
import com.syaroful.agrilinkvocpro.data.UserPreferences
|
import com.syaroful.agrilinkvocpro.data.UserPreferences
|
||||||
import com.syaroful.agrilinkvocpro.data.model.LoginResponse
|
import com.syaroful.agrilinkvocpro.data.model.LoginResponse
|
||||||
import com.syaroful.agrilinkvocpro.data.repository.AuthRepository
|
import com.syaroful.agrilinkvocpro.data.repository.AuthRepository
|
||||||
|
|
@ -55,24 +56,4 @@ class LoginViewModel(
|
||||||
fun resetLoginState() {
|
fun resetLoginState() {
|
||||||
_loginState.value = ResultState.Idle
|
_loginState.value = ResultState.Idle
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mapToUserFriendlyError(e: Exception): String {
|
|
||||||
return when (e) {
|
|
||||||
is java.net.UnknownHostException -> "Tidak dapat terhubung ke server. Periksa koneksi internet Anda."
|
|
||||||
is java.net.SocketTimeoutException -> "Waktu koneksi habis. Mohon coba lagi."
|
|
||||||
is java.io.IOException -> "Terjadi kesalahan jaringan. Silakan cek koneksi Anda."
|
|
||||||
is retrofit2.HttpException -> {
|
|
||||||
// Kamu bisa cek kode HTTP di sini juga kalau mau
|
|
||||||
when (e.code()) {
|
|
||||||
401 -> "Akses ditolak. Silakan periksa kredensial login Anda."
|
|
||||||
403 -> "Anda tidak memiliki izin untuk mengakses ini."
|
|
||||||
404 -> "Data tidak ditemukan."
|
|
||||||
500 -> "Terjadi kesalahan pada server. Silakan coba lagi nanti."
|
|
||||||
else -> "Terjadi kesalahan. Kode: ${e.code()}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> "Terjadi kesalahan. Silakan coba lagi."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,16 +12,25 @@ import androidx.compose.foundation.layout.imePadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
import androidx.compose.material.icons.filled.Clear
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SnackbarHost
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
|
@ -30,23 +39,43 @@ import com.syaroful.agrilinkvocpro.R
|
||||||
import com.syaroful.agrilinkvocpro.core.components.AppButton
|
import com.syaroful.agrilinkvocpro.core.components.AppButton
|
||||||
import com.syaroful.agrilinkvocpro.core.components.AppPasswordField
|
import com.syaroful.agrilinkvocpro.core.components.AppPasswordField
|
||||||
import com.syaroful.agrilinkvocpro.core.components.AppTextField
|
import com.syaroful.agrilinkvocpro.core.components.AppTextField
|
||||||
|
import com.syaroful.agrilinkvocpro.core.components.CustomSnackBarComponent
|
||||||
import com.syaroful.agrilinkvocpro.core.components.textTheme
|
import com.syaroful.agrilinkvocpro.core.components.textTheme
|
||||||
|
import com.syaroful.agrilinkvocpro.core.utils.ResultState
|
||||||
import com.syaroful.agrilinkvocpro.presentation.theme.DarkGrey
|
import com.syaroful.agrilinkvocpro.presentation.theme.DarkGrey
|
||||||
import com.syaroful.agrilinkvocpro.presentation.theme.MainGreen
|
import com.syaroful.agrilinkvocpro.presentation.theme.MainGreen
|
||||||
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RegisterScreen(
|
fun RegisterScreen(
|
||||||
onLoginSuccess: () -> Unit,
|
registerViewModel: RegisterViewModel = koinViewModel(),
|
||||||
|
onRegisterSuccess: () -> Unit,
|
||||||
onNavigateToLogin: () -> Unit,
|
onNavigateToLogin: () -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val signupState by registerViewModel.signupState.collectAsState()
|
||||||
|
|
||||||
|
val focusManager = LocalFocusManager.current
|
||||||
|
|
||||||
|
val snackBarHostState = remember { SnackbarHostState() }
|
||||||
|
|
||||||
var name by remember { mutableStateOf("") }
|
var name by remember { mutableStateOf("") }
|
||||||
var email by remember { mutableStateOf("") }
|
var email by remember { mutableStateOf("") }
|
||||||
var username by remember { mutableStateOf("") }
|
var username by remember { mutableStateOf("") }
|
||||||
var password by remember { mutableStateOf("") }
|
var password by remember { mutableStateOf("") }
|
||||||
var phone by remember { mutableStateOf("") }
|
|
||||||
|
|
||||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
Scaffold(
|
||||||
|
snackbarHost = {
|
||||||
|
SnackbarHost(hostState = snackBarHostState) { data ->
|
||||||
|
CustomSnackBarComponent(
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
message = data.visuals.message,
|
||||||
|
icon = if (data.visuals.message.contains("Failed")) Icons.Default.Clear else Icons.Default.Check
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) { innerPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
|
|
@ -108,8 +137,18 @@ fun RegisterScreen(
|
||||||
)
|
)
|
||||||
|
|
||||||
AppButton(
|
AppButton(
|
||||||
label = "Daftar",
|
label = if (signupState is ResultState.Loading) "Memuat..." else "Daftar",
|
||||||
|
isEnable = signupState !is ResultState.Loading,
|
||||||
) {
|
) {
|
||||||
|
focusManager.clearFocus()
|
||||||
|
registerViewModel.signup(name, username, email, password)
|
||||||
|
}
|
||||||
|
if (signupState is ResultState.Error) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
text = (signupState as ResultState.Error).message,
|
||||||
|
color = Color.Red
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
|
|
@ -128,6 +167,17 @@ fun RegisterScreen(
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(signupState) {
|
||||||
|
if (signupState is ResultState.Success<*>) {
|
||||||
|
snackBarHostState.showSnackbar(message = "Pendaftaran Akun Berhasil")
|
||||||
|
// Navigate to Home if Success
|
||||||
|
onRegisterSuccess()
|
||||||
|
registerViewModel.resetRegisterState()
|
||||||
|
} else if (signupState is ResultState.Error) {
|
||||||
|
snackBarHostState.showSnackbar(message = (signupState as ResultState.Error).message)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,26 +3,39 @@ package com.syaroful.agrilinkvocpro.presentation.screen.register
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.syaroful.agrilinkvocpro.core.utils.ResultState
|
import com.syaroful.agrilinkvocpro.core.utils.ResultState
|
||||||
import com.syaroful.agrilinkvocpro.data.model.LoginResponse
|
import com.syaroful.agrilinkvocpro.core.utils.extention.mapToUserFriendlyError
|
||||||
|
import com.syaroful.agrilinkvocpro.data.model.RegisterResponse
|
||||||
import com.syaroful.agrilinkvocpro.data.repository.AuthRepository
|
import com.syaroful.agrilinkvocpro.data.repository.AuthRepository
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class RegisterViewModel(
|
class RegisterViewModel(
|
||||||
authRepository: AuthRepository,
|
private val authRepository: AuthRepository,
|
||||||
) : ViewModel(){
|
) : ViewModel() {
|
||||||
private val _signupState = MutableStateFlow<ResultState<LoginResponse>>(ResultState.Idle)
|
private val _signupState = MutableStateFlow<ResultState<RegisterResponse>>(ResultState.Idle)
|
||||||
val signupState: StateFlow<ResultState<LoginResponse>> = _signupState
|
val signupState: StateFlow<ResultState<RegisterResponse>> = _signupState
|
||||||
|
|
||||||
fun signup(name: String, username: String, email: String, password: String){
|
fun signup(name: String, username: String, email: String, password: String) {
|
||||||
_signupState.value = ResultState.Loading
|
_signupState.value = ResultState.Loading
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
|
val response = authRepository.register(username, password, email, name)
|
||||||
}catch (e: Exception){
|
if (response.isSuccessful) {
|
||||||
|
_signupState.value = ResultState.Success(response.body())
|
||||||
|
} else {
|
||||||
|
val errorMessage = response.body()?.message ?: "Register Failed"
|
||||||
|
_signupState.value = ResultState.Error(errorMessage)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
val errorMessage = mapToUserFriendlyError(e)
|
||||||
|
_signupState.value = ResultState.Error(errorMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun resetRegisterState(){
|
||||||
|
_signupState.value = ResultState.Idle
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package com.syaroful.agrilinkvocpro.control_feature.di
|
package com.syaroful.agrilinkvocpro.control_feature.di
|
||||||
|
|
||||||
import com.syaroful.agrilinkvocpro.control_feature.presentation.control.ControlViewModel
|
import com.syaroful.agrilinkvocpro.control_feature.presentation.control.ControlViewModel
|
||||||
|
import com.syaroful.agrilinkvocpro.control_feature.presentation.history.ControlHistoryViewModel
|
||||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
val viewModelModule = module {
|
val viewModelModule = module {
|
||||||
viewModel { ControlViewModel(get(), get()) }
|
viewModel { ControlViewModel(get(), get()) }
|
||||||
|
viewModel { ControlHistoryViewModel(get(), get()) }
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user