login success

This commit is contained in:
shaulascr
2025-03-11 11:40:56 +07:00
parent c799acc0e2
commit e8f2284a36
15 changed files with 176 additions and 45 deletions

View File

@ -23,7 +23,7 @@ android {
buildTypes {
release {
buildConfigField("String", "BASE_URL", "\"http://192.168.1.6:3000/\"")
buildConfigField("String", "BASE_URL", "\"http://192.168.1.3:3000/\"")
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
@ -31,7 +31,7 @@ android {
)
}
debug {
buildConfigField("String", "BASE_URL", "\"http://192.168.1.6:3000/\"")
buildConfigField("String", "BASE_URL", "\"http://192.168.1.3:3000/\"")
}
}
compileOptions {

View File

@ -1,6 +1,8 @@
package com.alya.ecommerce_serang.data.api.dto
import com.google.gson.annotations.SerializedName
data class LoginRequest (
val email: String,
val password: String,
@SerializedName("emailOrPhone") val email: String,
@SerializedName("password") val password: String,
)

View File

@ -1,12 +1,14 @@
package com.alya.ecommerce_serang.data.api.dto
import com.google.gson.annotations.SerializedName
data class RegisterRequest (
val name: String?,
val email: String?,
val password: String?,
val username: String?,
val phone: String?,
val birthDate: String?,
val image: String?,
@SerializedName("birth_date") val birthDate: String?,
@SerializedName("userimg") val image: String?,
val otp: String? = null
)

View File

@ -11,6 +11,7 @@ import retrofit2.converter.gson.GsonConverterFactory
class ApiConfig {
companion object {
fun getApiService(tokenManager: SessionManager): ApiService {
val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}

View File

@ -28,12 +28,12 @@ interface ApiService {
):OtpResponse
@POST("login")
fun login(
suspend fun login(
@Body loginRequest: LoginRequest
): Call<LoginResponse>
): Response<LoginResponse>
@GET("product")
fun getAllProduct(): Call<AllProductResponse>
suspend fun getAllProduct(): Response<AllProductResponse>
@GET("product/detail/{id}")
fun getDetailProduct (

View File

@ -11,10 +11,7 @@ class ProductRepository(private val apiService: ApiService) {
withContext(Dispatchers.IO) {
try {
Log.d("ProductRepository", "Attempting to fetch products")
val response = apiService.getAllProduct().execute()
Log.d("ProductRepository", "Response received. Success: ${response.isSuccessful}")
Log.d("ProductRepository", "Response code: ${response.code()}")
Log.d("ProductRepository", "Response message: ${response.message()}")
val response = apiService.getAllProduct()
if (response.isSuccessful) {
// Return a Result.Success with the list of products
@ -28,4 +25,8 @@ class ProductRepository(private val apiService: ApiService) {
Result.Error(e)
}
}
// suspend fun getUserData(): Response<UserResponse> {
// return apiService.getProtectedData()
// }
}

View File

@ -1,7 +1,9 @@
package com.alya.ecommerce_serang.data.repository
import com.alya.ecommerce_serang.data.api.dto.LoginRequest
import com.alya.ecommerce_serang.data.api.dto.OtpRequest
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
import com.alya.ecommerce_serang.data.api.response.LoginResponse
import com.alya.ecommerce_serang.data.api.response.OtpResponse
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
@ -25,6 +27,21 @@ class UserRepository(private val apiService: ApiService) {
throw Exception("Registration failed: ${response.errorBody()?.string()}")
}
}
suspend fun login(email: String, password: String): Result<LoginResponse> {
return try {
val response = apiService.login(LoginRequest(email, password))
if (response.isSuccessful) {
response.body()?.let {
Result.Success(it)
} ?: Result.Error(Exception("Login response is empty"))
} else {
Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error"))
}
} catch (e: Exception) {
Result.Error(e)
}
}
}
sealed class Result<out T> {

View File

@ -6,11 +6,16 @@ import androidx.core.view.isVisible
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
import com.alya.ecommerce_serang.databinding.ActivityMainBinding
import com.alya.ecommerce_serang.utils.SessionManager
//@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var apiService: ApiService
private lateinit var sessionManager: SessionManager
private val navController by lazy {
(supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment).navController
}
@ -19,6 +24,9 @@ class MainActivity : AppCompatActivity() {
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
sessionManager = SessionManager(this)
apiService = ApiConfig.getApiService(sessionManager) // Inject SessionManager
setupBottomNavigation()
observeDestinationChanges()

View File

@ -1,21 +1,75 @@
package com.alya.ecommerce_serang.ui.auth
import android.content.Intent
import android.os.Bundle
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.WindowInsetsCompat
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
import com.alya.ecommerce_serang.data.repository.Result
import com.alya.ecommerce_serang.data.repository.UserRepository
import com.alya.ecommerce_serang.databinding.ActivityLoginBinding
import com.alya.ecommerce_serang.ui.MainActivity
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
class LoginActivity : AppCompatActivity() {
private lateinit var binding: ActivityLoginBinding
private val loginViewModel: LoginViewModel by viewModels{
BaseViewModelFactory {
val apiService = ApiConfig.getUnauthenticatedApiService()
val userRepository = UserRepository(apiService)
LoginViewModel(userRepository)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_login)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)
setupListeners()
observeLoginState()
}
private fun setupListeners() {
binding.btnLogin.setOnClickListener {
val email = binding.etLoginEmail.text.toString()
val password = binding.etLoginPassword.text.toString()
if (email.isEmpty() || password.isEmpty()) {
Toast.makeText(this, "Please fill in all fields", Toast.LENGTH_SHORT).show()
} else {
loginViewModel.login(email, password)
}
}
}
private fun observeLoginState() {
loginViewModel.loginState.observe(this) { result ->
when (result) {
is com.alya.ecommerce_serang.data.repository.Result.Success -> {
val accessToken = result.data.accessToken
val sessionManager = SessionManager(this)
sessionManager.saveToken(accessToken)
Toast.makeText(this, "Login Successful", Toast.LENGTH_SHORT).show()
startActivity(Intent(this, MainActivity::class.java))
finish()
}
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
Toast.makeText(this, "Login Failed: ${result.exception.message}", Toast.LENGTH_LONG).show()
}
is Result.Loading -> {
// Show loading state
}
}
}
}
}

View File

@ -0,0 +1,23 @@
package com.alya.ecommerce_serang.ui.auth
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.alya.ecommerce_serang.data.api.response.LoginResponse
import com.alya.ecommerce_serang.data.repository.Result
import com.alya.ecommerce_serang.data.repository.UserRepository
import kotlinx.coroutines.launch
class LoginViewModel(private val repository: UserRepository) : ViewModel() {
private val _loginState = MutableLiveData<com.alya.ecommerce_serang.data.repository.Result<LoginResponse>>()
val loginState: LiveData<Result<LoginResponse>> get() = _loginState
fun login(email: String, password: String) {
viewModelScope.launch {
_loginState.value = com.alya.ecommerce_serang.data.repository.Result.Loading
val result = repository.login(email, password)
_loginState.value = result
}
}
}

View File

@ -1,5 +1,6 @@
package com.alya.ecommerce_serang.ui.auth
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
@ -32,9 +33,6 @@ class RegisterActivity : AppCompatActivity() {
// Observe OTP state
observeOtpState()
// Observe Register state
observeRegisterState()
binding.btnSignup.setOnClickListener {
// Retrieve values inside the click listener (so we get latest input)
val birthDate = binding.etBirthDate.text.toString()
@ -74,6 +72,14 @@ class RegisterActivity : AppCompatActivity() {
}
}
}
// Observe Register state
observeRegisterState()
}
binding.tvLoginAlt.setOnClickListener{
val intent = Intent(this, LoginActivity::class.java)
startActivity(intent)
}
}
@ -110,6 +116,8 @@ class RegisterActivity : AppCompatActivity() {
// 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 -> {

View File

@ -25,6 +25,8 @@ class HomeFragment : Fragment() {
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
private var productAdapter: HorizontalProductAdapter? = null
private lateinit var sessionManager: SessionManager
private val viewModel: HomeViewModel by viewModels {
BaseViewModelFactory {
val apiService = ApiConfig.getApiService(sessionManager)
@ -32,18 +34,18 @@ class HomeFragment : Fragment() {
HomeViewModel(productRepository)
}
}
private var productAdapter: HorizontalProductAdapter? = null
private lateinit var sessionManager: SessionManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sessionManager = SessionManager(requireContext())
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
sessionManager = SessionManager(requireContext()) // Initialize SessionManager
return binding.root
return _binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

View File

@ -17,7 +17,6 @@ class HomeViewModel (
private val _uiState = MutableStateFlow<HomeUiState>(HomeUiState.Loading)
val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()
init {
loadProducts()
}
@ -26,20 +25,16 @@ class HomeViewModel (
viewModelScope.launch {
_uiState.value = HomeUiState.Loading
productRepository.getAllProducts().let { result ->
when (result) {
is Result.Success -> {
// Handle the success case
_uiState.value = HomeUiState.Success(result.data) // result.data contains the list of products
}
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
// Handle the error case
_uiState.value = HomeUiState.Error(result.exception.message ?: "Unknown error")
Log.e("HomeViewModel", "Failed to fetch products", result.exception)
}
com.alya.ecommerce_serang.data.repository.Result.Loading -> {
// Optionally handle the loading state if needed
}
when (val result = productRepository.getAllProducts()) {
is Result.Success -> {
_uiState.value = HomeUiState.Success(result.data)
}
is Result.Error -> {
_uiState.value = HomeUiState.Error(result.exception.message ?: "Unknown error")
Log.e("HomeViewModel", "Failed to fetch products", result.exception)
}
is Result.Loading-> {
}
}
}
@ -49,6 +44,23 @@ class HomeViewModel (
fun retry() {
loadProducts()
}
// private fun fetchUserData() {
// viewModelScope.launch {
// try {
// val response = apiService.getProtectedData() // Example API request
// if (response.isSuccessful) {
// val data = response.body()
// Log.d("HomeFragment", "User Data: $data")
// // Update UI with data
// } else {
// Log.e("HomeFragment", "Error: ${response.message()}")
// }
// } catch (e: Exception) {
// Log.e("HomeFragment", "Exception: ${e.message}")
// }
// }
// }
}
sealed class HomeUiState {

View File

@ -12,7 +12,7 @@ class SessionManager(context: Context) {
private const val USER_TOKEN = "user_token"
}
fun saveAuthToken(token: String) {
fun saveToken(token: String) {
val editor = sharedPreferences.edit()
editor.putString(USER_TOKEN, token)
editor.apply()

View File

@ -75,6 +75,7 @@
android:layout_marginBottom="16dp"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/login"