feat: implement register feature

This commit is contained in:
Cutiful 2025-07-05 11:26:07 +07:00
parent 90df005a3f
commit fc9c9614ba
7 changed files with 88 additions and 34 deletions

View File

@ -0,0 +1,6 @@
package com.syaroful.agrilinkvocpro.data.model
data class RegisterResponse(
val message: String?,
val success: Boolean?
)

View File

@ -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.login.LoginViewModel
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.dsl.module
@ -14,4 +15,5 @@ val viewModelModule = module {
viewModel { ProfileViewModel(get()) }
viewModel { HomeViewModel(get(), get()) }
viewModel { DetailViewModel(get(), get()) }
viewModel { RegisterViewModel(get()) }
}

View File

@ -55,7 +55,7 @@ fun SetupNavigation(
}
composable("register") {
RegisterScreen(
onLoginSuccess = {
onRegisterSuccess = {
navController.navigate("login") {
popUpTo("login") { inclusive = true }
}

View File

@ -3,6 +3,7 @@ package com.syaroful.agrilinkvocpro.presentation.screen.login
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
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.model.LoginResponse
import com.syaroful.agrilinkvocpro.data.repository.AuthRepository
@ -55,24 +56,4 @@ class LoginViewModel(
fun resetLoginState() {
_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."
}
}
}

View File

@ -12,16 +12,25 @@ import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
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.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.KeyboardType
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.AppPasswordField
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.utils.ResultState
import com.syaroful.agrilinkvocpro.presentation.theme.DarkGrey
import com.syaroful.agrilinkvocpro.presentation.theme.MainGreen
import org.koin.androidx.compose.koinViewModel
@Composable
fun RegisterScreen(
onLoginSuccess: () -> Unit,
registerViewModel: RegisterViewModel = koinViewModel(),
onRegisterSuccess: () -> Unit,
onNavigateToLogin: () -> Unit,
) {
val signupState by registerViewModel.signupState.collectAsState()
val focusManager = LocalFocusManager.current
val snackBarHostState = remember { SnackbarHostState() }
var name by remember { mutableStateOf("") }
var email by remember { mutableStateOf("") }
var username 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(
modifier = Modifier
.verticalScroll(rememberScrollState())
@ -108,8 +137,18 @@ fun RegisterScreen(
)
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(
@ -128,6 +167,17 @@ fun RegisterScreen(
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)
}
}
}
}
}

View File

@ -3,26 +3,39 @@ package com.syaroful.agrilinkvocpro.presentation.screen.register
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
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 kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
class RegisterViewModel(
authRepository: AuthRepository,
private val authRepository: AuthRepository,
) : ViewModel() {
private val _signupState = MutableStateFlow<ResultState<LoginResponse>>(ResultState.Idle)
val signupState: StateFlow<ResultState<LoginResponse>> = _signupState
private val _signupState = MutableStateFlow<ResultState<RegisterResponse>>(ResultState.Idle)
val signupState: StateFlow<ResultState<RegisterResponse>> = _signupState
fun signup(name: String, username: String, email: String, password: String) {
_signupState.value = ResultState.Loading
viewModelScope.launch {
try {
val response = authRepository.register(username, password, email, name)
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
}
}
}
}

View File

@ -1,9 +1,11 @@
package com.syaroful.agrilinkvocpro.control_feature.di
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.dsl.module
val viewModelModule = module {
viewModel { ControlViewModel(get(), get()) }
viewModel { ControlHistoryViewModel(get(), get()) }
}