From e8f2284a36a6c386ebfdaa8154d0908cdcc8b035 Mon Sep 17 00:00:00 2001 From: shaulascr Date: Tue, 11 Mar 2025 11:40:56 +0700 Subject: [PATCH] login success --- app/build.gradle.kts | 4 +- .../data/api/dto/LoginRequest.kt | 6 +- .../data/api/dto/RegisterRequest.kt | 6 +- .../data/api/retrofit/ApiConfig.kt | 1 + .../data/api/retrofit/ApiService.kt | 6 +- .../data/repository/ProductRepository.kt | 9 +-- .../data/repository/UserRepository.kt | 17 +++++ .../alya/ecommerce_serang/ui/MainActivity.kt | 8 +++ .../ecommerce_serang/ui/auth/LoginActivity.kt | 70 ++++++++++++++++--- .../ui/auth/LoginViewModel.kt | 23 ++++++ .../ui/auth/RegisterActivity.kt | 14 +++- .../ecommerce_serang/ui/home/HomeFragment.kt | 12 ++-- .../ecommerce_serang/ui/home/HomeViewModel.kt | 42 +++++++---- .../ecommerce_serang/utils/SessionManager.kt | 2 +- app/src/main/res/layout/activity_login.xml | 1 + 15 files changed, 176 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/auth/LoginViewModel.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 724cb49..cc9e2b6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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 { diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/LoginRequest.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/LoginRequest.kt index bcf9afb..3f98cc8 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/LoginRequest.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/LoginRequest.kt @@ -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, ) \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/RegisterRequest.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/RegisterRequest.kt index 0e38c12..650eab3 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/RegisterRequest.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/RegisterRequest.kt @@ -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 ) \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiConfig.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiConfig.kt index 43720e3..e8ddcb0 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiConfig.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiConfig.kt @@ -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 } diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt index ca78eab..7981961 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt @@ -28,12 +28,12 @@ interface ApiService { ):OtpResponse @POST("login") - fun login( + suspend fun login( @Body loginRequest: LoginRequest - ): Call + ): Response @GET("product") - fun getAllProduct(): Call + suspend fun getAllProduct(): Response @GET("product/detail/{id}") fun getDetailProduct ( diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/ProductRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/ProductRepository.kt index 47f561c..d43462f 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/repository/ProductRepository.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/ProductRepository.kt @@ -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 { +// return apiService.getProtectedData() +// } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/UserRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/UserRepository.kt index e7e57ac..335cea9 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/repository/UserRepository.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/UserRepository.kt @@ -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 { + 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 { diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/MainActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/MainActivity.kt index 9450c13..3877327 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/MainActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/MainActivity.kt @@ -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() diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/auth/LoginActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/auth/LoginActivity.kt index 673762e..9ae19c4 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/auth/LoginActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/auth/LoginActivity.kt @@ -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 + } + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/auth/LoginViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/auth/LoginViewModel.kt new file mode 100644 index 0000000..8b4c27d --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/auth/LoginViewModel.kt @@ -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>() + val loginState: LiveData> 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 + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/auth/RegisterActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/auth/RegisterActivity.kt index d7991a1..5a10983 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/auth/RegisterActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/auth/RegisterActivity.kt @@ -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 -> { diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/home/HomeFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/home/HomeFragment.kt index 975b4c9..08db098 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/home/HomeFragment.kt @@ -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?) { diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/home/HomeViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/home/HomeViewModel.kt index 3b087cf..14e81af 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/home/HomeViewModel.kt @@ -17,7 +17,6 @@ class HomeViewModel ( private val _uiState = MutableStateFlow(HomeUiState.Loading) val uiState: StateFlow = _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 { diff --git a/app/src/main/java/com/alya/ecommerce_serang/utils/SessionManager.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/SessionManager.kt index fcb3da6..52b405b 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/utils/SessionManager.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/utils/SessionManager.kt @@ -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() diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index d942468..7510d73 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -75,6 +75,7 @@ android:layout_marginBottom="16dp"/>