diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 9b1c4b11..088f3dd7 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -9,8 +9,6 @@
-
-
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 00000000..ff9696e1
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index df532fb7..d50e300f 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,3 @@
-
-
+
diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/SignInFragment.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/SignInFragment.kt
deleted file mode 100644
index 4997cd52..00000000
--- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/SignInFragment.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.hieuwu.groceriesstore.presentation.authentication
-
-import android.annotation.SuppressLint
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.databinding.DataBindingUtil
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import androidx.navigation.findNavController
-import com.hieuwu.groceriesstore.R
-import com.hieuwu.groceriesstore.databinding.FragmentSigninBinding
-import com.hieuwu.groceriesstore.utilities.showMessageSnackBar
-import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.coroutines.launch
-
-@AndroidEntryPoint
-class SignInFragment : Fragment() {
-
- private lateinit var binding: FragmentSigninBinding
-
- private val viewModel: SignInViewModel by viewModels()
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- binding = DataBindingUtil.inflate(
- inflater, R.layout.fragment_signin, container, false
- )
-
- binding.signInViewModel = viewModel
- binding.lifecycleOwner = this
-
- setObserver()
- setEventListener()
-
- return binding.root
- }
-
- @SuppressLint("UnsafeRepeatOnLifecycleDetector")
- private fun setObserver() {
- viewLifecycleOwner.lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
- viewModel.showAccountNotExistedError.collect {
- showMessageSnackBar("Account is not existed")
- }
- }
-
- launch {
- viewModel.isSignUpSuccessful.collect {
- if (it == true) {
- activity?.finish()
- }
- }
- }
- }
- }
- }
-
- private fun setEventListener() {
- with(binding) {
- signUpTextview.setOnClickListener {
- view?.findNavController()?.navigate(R.id.action_signInFragment_to_signUpFragment)
- }
- signinButton.setOnClickListener {
- viewModel.signIn()
- }
- }
- }
-}
diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/SignInViewModel.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/SignInViewModel.kt
deleted file mode 100644
index 1e3d8889..00000000
--- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/SignInViewModel.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.hieuwu.groceriesstore.presentation.authentication
-
-import androidx.databinding.Bindable
-import androidx.lifecycle.viewModelScope
-import com.hieuwu.groceriesstore.BR
-import com.hieuwu.groceriesstore.domain.usecases.SignInUseCase
-import com.hieuwu.groceriesstore.presentation.utils.ObservableViewModel
-import dagger.hilt.android.lifecycle.HiltViewModel
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.SharedFlow
-import kotlinx.coroutines.launch
-import javax.inject.Inject
-
-@HiltViewModel
-class SignInViewModel @Inject constructor(
- private val signInUseCase: SignInUseCase
-) : ObservableViewModel() {
-
- private val _isSignUpSuccessful = MutableSharedFlow()
- val isSignUpSuccessful: SharedFlow = _isSignUpSuccessful
-
- private val _showAccountNotExistedError = MutableSharedFlow()
- val showAccountNotExistedError: SharedFlow = _showAccountNotExistedError
-
- private var _email: String? = null
- var email: String?
- @Bindable
- get() {
- return _email
- }
- set(value) {
- _email = value
- notifyPropertyChanged(BR.email)
- }
-
- private var _password: String? = null
- var password: String?
- @Bindable
- get() {
- return _password
- }
- set(value) {
- _password = value
- notifyPropertyChanged(BR.password)
- }
-
- fun signIn() {
- viewModelScope.launch {
- val res = signInUseCase.execute(
- SignInUseCase.Input(
- email = _email!!,
- password = _password!!
- )
- )
-
- when (res) {
- is SignInUseCase.Output.Error -> {
- //TODO Handle show general error
- }
- is SignInUseCase.Output.AccountNotExistedError -> {
- _showAccountNotExistedError.emit(true)
- _isSignUpSuccessful.emit(false)
- }
- else -> {
- _isSignUpSuccessful.emit(true)
- }
- }
-
- }
- }
-}
diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/SignUpFragment.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/SignUpFragment.kt
deleted file mode 100644
index 3dc0ba58..00000000
--- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/SignUpFragment.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.hieuwu.groceriesstore.presentation.authentication
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.databinding.DataBindingUtil
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import com.hieuwu.groceriesstore.LoadingDialog
-import com.hieuwu.groceriesstore.R
-import com.hieuwu.groceriesstore.databinding.FragmentSignUpBinding
-import com.hieuwu.groceriesstore.utilities.showMessageSnackBar
-import dagger.hilt.android.AndroidEntryPoint
-
-@AndroidEntryPoint
-class SignUpFragment : Fragment() {
-
- private lateinit var loadingDialog: LoadingDialog
- private lateinit var binding: FragmentSignUpBinding
-
- private val viewModel: SignUpViewModel by viewModels()
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- binding = DataBindingUtil.inflate(
- inflater, R.layout.fragment_sign_up, container, false
- )
- loadingDialog = LoadingDialog(requireActivity())
-
- binding.signUpViewModel = viewModel
- binding.lifecycleOwner = this
-
- setObserver()
- setEventListener()
-
- return binding.root
- }
-
- private fun setObserver() {
- viewModel.isSignUpSuccessful?.observe(viewLifecycleOwner) {
- loadingDialog.show()
- if (it != null) {
-
- if (it == true) showMessageSnackBar(getString(R.string.authentication_successfully))
- else showMessageSnackBar(getString(R.string.authentication_failed))
-
- loadingDialog.dismiss()
- }
- }
- }
-
- private fun setEventListener() {
- binding.signupBtn.setOnClickListener {
- viewModel.createAccount()
- }
- }
-}
diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/SignUpViewModel.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/SignUpViewModel.kt
deleted file mode 100644
index 6c663e28..00000000
--- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/SignUpViewModel.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.hieuwu.groceriesstore.presentation.authentication
-
-import androidx.databinding.Bindable
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.viewModelScope
-import com.hieuwu.groceriesstore.BR
-import com.hieuwu.groceriesstore.data.repository.UserRepository
-import com.hieuwu.groceriesstore.presentation.utils.ObservableViewModel
-import dagger.hilt.android.lifecycle.HiltViewModel
-import javax.inject.Inject
-import kotlinx.coroutines.launch
-
-@HiltViewModel
-class SignUpViewModel @Inject constructor(private val userRepository: UserRepository) : ObservableViewModel() {
-
- private var _email: String? = null
- var email: String?
- @Bindable
- get() = _email
- set(value) {
- _email = value
- notifyPropertyChanged(BR.email)
- }
-
- private var _password: String? = null
- var password: String?
- @Bindable
- get() = _password
- set(value) {
- _password = value
- notifyPropertyChanged(BR.password)
- }
-
- private var _name: String? = null
- var name: String?
- @Bindable
- get() = _name
- set(value) {
- _name = value
- notifyPropertyChanged(BR.name)
- }
-
- // Declare check variable as live data, set it when create success fully
- // In fragment observe it
- private val _isSignUpSuccessful = MutableLiveData()
- val isSignUpSuccessful: LiveData
- get() = _isSignUpSuccessful
-
- fun createAccount() {
- viewModelScope.launch {
- _isSignUpSuccessful.value = userRepository.createAccount(_email!!, _password!!, _name!!)
- }
- }
-}
diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/composables/IconTextInput.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/composables/IconTextInput.kt
new file mode 100644
index 00000000..5bffac04
--- /dev/null
+++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/composables/IconTextInput.kt
@@ -0,0 +1,86 @@
+package com.hieuwu.groceriesstore.presentation.authentication.composables
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.Text
+import androidx.compose.material.TextField
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Email
+import androidx.compose.material.icons.rounded.Backspace
+import androidx.compose.material3.Icon
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.hieuwu.groceriesstore.R
+
+@Composable
+fun IconTextInput(
+ leadingIcon: ImageVector,
+ trailingIcon: ImageVector,
+ modifier: Modifier = Modifier,
+ value: String,
+ placeholder: String,
+ type: KeyboardType = KeyboardType.Email,
+ visualTransformation: VisualTransformation = VisualTransformation.None,
+ onValueChange: (String) -> Unit,
+ onTrailingIconClick: () -> Unit
+) {
+ TextField(
+ keyboardOptions = KeyboardOptions(keyboardType = type),
+ modifier = modifier
+ .fillMaxWidth()
+ .border(
+ border = BorderStroke(
+ width = 1.dp,
+ color = colorResource(id = R.color.colorPrimary),
+
+ ),
+ shape = RoundedCornerShape(6.dp)
+ ),
+ value = value,
+ onValueChange = { text -> onValueChange(text) },
+ visualTransformation = visualTransformation,
+ leadingIcon = {
+ Icon(
+ imageVector = leadingIcon,
+ contentDescription = null,
+ tint = colorResource(id = R.color.primary_button)
+ )
+ },
+ trailingIcon = {
+ Icon(
+ modifier = modifier.clickable {
+ onTrailingIconClick()
+ },
+ imageVector = trailingIcon,
+ contentDescription = null,
+ tint = Color.DarkGray
+ )
+ },
+ placeholder = {
+ Text(text = placeholder, color = colorResource(id = R.color.colorPrimary))
+ })
+}
+
+@Preview
+@Composable
+fun IconTextInputPreview() {
+ IconTextInput(
+ leadingIcon = Icons.Filled.Email,
+ trailingIcon = Icons.Rounded.Backspace,
+ value = "Please input your email",
+ placeholder = "",
+ onTrailingIconClick = {},
+ onValueChange = {}
+ )
+}
diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signin/SignInFragment.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signin/SignInFragment.kt
new file mode 100644
index 00000000..41503c88
--- /dev/null
+++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signin/SignInFragment.kt
@@ -0,0 +1,35 @@
+package com.hieuwu.groceriesstore.presentation.authentication.signin
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.Fragment
+import androidx.navigation.findNavController
+import com.hieuwu.groceriesstore.R
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class SignInFragment : Fragment() {
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return ComposeView(requireContext()).apply {
+ setContent {
+ SignInScreen(
+ modifier = Modifier.fillMaxSize(),
+ onSignUpClick = {
+ view?.findNavController()
+ ?.navigate(R.id.action_signInFragment_to_signUpFragment)
+ }
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signin/SignInScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signin/SignInScreen.kt
new file mode 100644
index 00000000..6e390330
--- /dev/null
+++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signin/SignInScreen.kt
@@ -0,0 +1,153 @@
+package com.hieuwu.groceriesstore.presentation.authentication.signin
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Backspace
+import androidx.compose.material.icons.filled.Email
+import androidx.compose.material.icons.filled.Lock
+import androidx.compose.material.icons.outlined.Visibility
+import androidx.compose.material.icons.outlined.VisibilityOff
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedButton
+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.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import com.hieuwu.groceriesstore.R
+import com.hieuwu.groceriesstore.presentation.authentication.composables.IconTextInput
+
+@Composable
+fun SignInScreen(
+ modifier: Modifier = Modifier,
+ onSignUpClick: () -> Unit,
+ viewModel: SignInViewModel = hiltViewModel()
+) {
+ val snackbarHostState = remember { SnackbarHostState() }
+ LaunchedEffect(Unit) {
+ viewModel
+ .showAccountNotExistedError
+ .collect {
+ snackbarHostState.showSnackbar("Account not existed!")
+ }
+ }
+
+ LaunchedEffect(Unit) {
+ viewModel.isSignUpSuccessful.collect {
+ snackbarHostState.showSnackbar("Sign in successfully!")
+ }
+ }
+
+ Scaffold(
+ snackbarHost = { SnackbarHost(snackbarHostState) },
+ ) { paddingValues ->
+ Box(
+ modifier = modifier
+ .padding(paddingValues)
+ .fillMaxWidth()
+ .fillMaxHeight(),
+ contentAlignment = Alignment.TopCenter
+ ) {
+ Image(
+ modifier = Modifier
+ .fillMaxWidth(),
+ painter = painterResource(id = R.drawable.login_background),
+ contentDescription = null
+ )
+
+ val email = viewModel.email.collectAsState()
+ val password = viewModel.password.collectAsState()
+
+ Column(
+ modifier = Modifier
+ .padding(top = 320.dp, start = 20.dp, end = 20.dp)
+ .fillMaxWidth()
+ .background(
+ color = Color.White,
+ shape = RoundedCornerShape(topStart = 48.dp, topEnd = 48.dp)
+ ),
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ Text(
+ text = "Sign In",
+ style = MaterialTheme.typography.headlineMedium
+ )
+ IconTextInput(
+ leadingIcon = Icons.Filled.Email,
+ trailingIcon = Icons.Filled.Backspace,
+ value = email.value,
+ placeholder = "Email",
+ onValueChange = {
+ viewModel.onEmailChange(it)
+ },
+ onTrailingIconClick = {
+ viewModel.onRemoveText()
+ },
+ )
+ val showPassword = remember { mutableStateOf(false) }
+ IconTextInput(
+ leadingIcon = Icons.Filled.Lock,
+ trailingIcon = if (showPassword.value) Icons.Outlined.Visibility else Icons.Outlined.VisibilityOff,
+ value = password.value,
+ placeholder = "Password",
+ type = KeyboardType.Password,
+ visualTransformation = if (showPassword.value) VisualTransformation.None else PasswordVisualTransformation(),
+ onValueChange = {
+ viewModel.onPasswordChange(it)
+ },
+ onTrailingIconClick = {
+ showPassword.value = !showPassword.value
+ },
+ )
+ Button(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = { viewModel.signIn() },
+ colors = ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.colorPrimary)),
+ ) {
+ Text("Sign in")
+ }
+ OutlinedButton(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = onSignUpClick,
+ colors = ButtonDefaults.buttonColors(
+ containerColor = Color.Transparent,
+ contentColor = colorResource(id = R.color.colorPrimary)),
+ ) {
+ Text("Sign up")
+ }
+ }
+ }
+ }
+
+}
+
+@Preview
+@Composable
+fun SignInScreenPreview() {
+ SignInScreen(onSignUpClick = {})
+}
diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signin/SignInViewModel.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signin/SignInViewModel.kt
new file mode 100644
index 00000000..5e7751e3
--- /dev/null
+++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signin/SignInViewModel.kt
@@ -0,0 +1,65 @@
+package com.hieuwu.groceriesstore.presentation.authentication.signin
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.hieuwu.groceriesstore.domain.usecases.SignInUseCase
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class SignInViewModel @Inject constructor(
+ private val signInUseCase: SignInUseCase
+) : ViewModel() {
+
+ private val _isSignUpSuccessful = MutableSharedFlow()
+ val isSignUpSuccessful: SharedFlow = _isSignUpSuccessful
+
+ private val _showAccountNotExistedError = MutableSharedFlow()
+ val showAccountNotExistedError: SharedFlow = _showAccountNotExistedError
+
+ private val _email = MutableStateFlow("")
+ val email: StateFlow = _email
+
+ private val _password = MutableStateFlow("")
+ val password: StateFlow = _password
+
+ fun onEmailChange(newEmail: String) {
+ _email.value = newEmail
+ }
+
+ fun onRemoveText() {
+ _email.value = ""
+ }
+
+ fun onPasswordChange(newPassword: String) {
+ _password.value = newPassword
+ }
+
+ fun signIn() {
+ viewModelScope.launch {
+ when (signInUseCase.execute(
+ SignInUseCase.Input(
+ email = _email.value,
+ password = _password.value
+ )
+ )) {
+ is SignInUseCase.Output.Error -> {
+ //TODO Handle show general error
+ }
+
+ is SignInUseCase.Output.AccountNotExistedError -> {
+ _showAccountNotExistedError.emit(Unit)
+ }
+
+ else -> {
+ _isSignUpSuccessful.emit(Unit)
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signup/SignUpFragment.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signup/SignUpFragment.kt
new file mode 100644
index 00000000..ac3a947c
--- /dev/null
+++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signup/SignUpFragment.kt
@@ -0,0 +1,29 @@
+package com.hieuwu.groceriesstore.presentation.authentication.signup
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.Fragment
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class SignUpFragment : Fragment() {
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ return ComposeView(requireContext()).apply {
+ setContent {
+ SignUpScreen(
+ modifier = Modifier.fillMaxSize(),
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signup/SignUpScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signup/SignUpScreen.kt
new file mode 100644
index 00000000..e04e0305
--- /dev/null
+++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signup/SignUpScreen.kt
@@ -0,0 +1,147 @@
+package com.hieuwu.groceriesstore.presentation.authentication.signup
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Backspace
+import androidx.compose.material.icons.filled.Email
+import androidx.compose.material.icons.filled.Lock
+import androidx.compose.material.icons.filled.Person
+import androidx.compose.material.icons.outlined.Visibility
+import androidx.compose.material.icons.outlined.VisibilityOff
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.MaterialTheme
+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.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import com.hieuwu.groceriesstore.R
+import com.hieuwu.groceriesstore.presentation.authentication.composables.IconTextInput
+
+@Composable
+fun SignUpScreen(modifier: Modifier = Modifier,
+ viewModel: SignUpViewModel = hiltViewModel()) {
+
+ val snackbarHostState = remember { SnackbarHostState() }
+
+ LaunchedEffect(Unit) {
+ viewModel.showSignUpSuccessMessage.collect {
+ snackbarHostState.showSnackbar("Sign in successfully!")
+ }
+ }
+
+ LaunchedEffect(Unit) {
+ viewModel.showSignUpErrorMessage.collect {
+ snackbarHostState.showSnackbar("Sign in failed :( Please try again!")
+ }
+ }
+
+
+ Scaffold(
+ snackbarHost = { SnackbarHost(snackbarHostState) },
+ ) { paddingValues ->
+ Box(
+ modifier = modifier
+ .padding(paddingValues)
+ .fillMaxWidth()
+ .fillMaxHeight(),
+ contentAlignment = Alignment.TopCenter
+ ) {
+ Image(
+ modifier = Modifier
+ .fillMaxWidth(),
+ painter = painterResource(id = R.drawable.login_background),
+ contentDescription = null
+ )
+
+ val email = viewModel.email.collectAsState()
+ val password = viewModel.password.collectAsState()
+
+ Column(
+ modifier = Modifier
+ .padding(top = 320.dp, start = 20.dp, end = 20.dp)
+ .fillMaxWidth()
+ .background(
+ color = Color.White,
+ shape = RoundedCornerShape(topStart = 48.dp, topEnd = 48.dp)
+ ),
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ Text(
+ text = "Sign Up",
+ style = MaterialTheme.typography.headlineMedium
+ )
+ IconTextInput(
+ leadingIcon = Icons.Filled.Person,
+ trailingIcon = Icons.Filled.Backspace,
+ value = email.value,
+ placeholder = "Name",
+ onValueChange = {
+ viewModel.onNameChange(it)
+ },
+ onTrailingIconClick = {
+ viewModel.onNameRemove()
+ },
+ )
+ IconTextInput(
+ leadingIcon = Icons.Filled.Email,
+ trailingIcon = Icons.Filled.Backspace,
+ value = email.value,
+ placeholder = "Email",
+ onValueChange = {
+ viewModel.onEmailChange(it)
+ },
+ onTrailingIconClick = {
+ viewModel.onEmailRemove()
+ },
+ )
+
+ val showPassword = remember { mutableStateOf(false) }
+ IconTextInput(
+ leadingIcon = Icons.Filled.Lock,
+ trailingIcon = if (showPassword.value) Icons.Outlined.Visibility else Icons.Outlined.VisibilityOff,
+ value = password.value,
+ placeholder = "Password",
+ type = KeyboardType.Password,
+ visualTransformation = if (showPassword.value) VisualTransformation.None else PasswordVisualTransformation(),
+ onValueChange = {
+ viewModel.onPasswordChange(it)
+ },
+ onTrailingIconClick = {
+ showPassword.value = !showPassword.value
+ },
+ )
+ Button(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = { viewModel.createAccount() },
+ colors = ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.colorPrimary)),
+ ) {
+ Text("Sign up")
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signup/SignUpViewModel.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signup/SignUpViewModel.kt
new file mode 100644
index 00000000..20fb8372
--- /dev/null
+++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/authentication/signup/SignUpViewModel.kt
@@ -0,0 +1,63 @@
+package com.hieuwu.groceriesstore.presentation.authentication.signup
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.hieuwu.groceriesstore.data.repository.UserRepository
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class SignUpViewModel @Inject constructor(private val userRepository: UserRepository) :
+ ViewModel() {
+
+ private var _email = MutableStateFlow("")
+ var email: StateFlow = _email
+
+ private var _password = MutableStateFlow("")
+ var password: StateFlow = _password
+
+ private var _name = MutableStateFlow("")
+ var name: StateFlow = _name
+
+ private val _showSignUpSuccessMessage = MutableSharedFlow()
+ val showSignUpSuccessMessage: SharedFlow = _showSignUpSuccessMessage
+
+ private val _showSignUpErrorMessage = MutableSharedFlow()
+ val showSignUpErrorMessage: SharedFlow = _showSignUpErrorMessage
+
+ fun onEmailChange(text: String) {
+ _email.value = text
+ }
+
+ fun onEmailRemove() {
+ _email.value = ""
+ }
+
+ fun onNameRemove() {
+ _name.value = ""
+ }
+
+ fun onPasswordChange(text: String) {
+ _password.value = text
+ }
+
+ fun onNameChange(text: String) {
+ _name.value = text
+ }
+
+ fun createAccount() {
+ viewModelScope.launch {
+ val result = userRepository.createAccount(_email.value, _password.value, _name.value)
+ if (result) {
+ _showSignUpSuccessMessage.emit(Unit)
+ } else {
+ _showSignUpErrorMessage.emit(Unit)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/orderhistory/OrderListViewModel.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/orderhistory/OrderListViewModel.kt
index 35ddc1a1..c5f24dfc 100644
--- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/orderhistory/OrderListViewModel.kt
+++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/orderhistory/OrderListViewModel.kt
@@ -5,14 +5,9 @@ import androidx.lifecycle.viewModelScope
import com.hieuwu.groceriesstore.domain.models.OrderModel
import com.hieuwu.groceriesstore.domain.usecases.GetOrderListUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import timber.log.Timber
import javax.inject.Inject
@HiltViewModel
diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt
index 02b408dc..89e08842 100644
--- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt
+++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/productdetail/ProductDetailScreen.kt
@@ -1,6 +1,5 @@
package com.hieuwu.groceriesstore.presentation.productdetail
-import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -32,7 +31,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.colorResource
-import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
diff --git a/app/src/main/res/layout/fragment_sign_up.xml b/app/src/main/res/layout/fragment_sign_up.xml
deleted file mode 100644
index b549da6d..00000000
--- a/app/src/main/res/layout/fragment_sign_up.xml
+++ /dev/null
@@ -1,144 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/fragment_signin.xml b/app/src/main/res/layout/fragment_signin.xml
deleted file mode 100644
index 51bcd67a..00000000
--- a/app/src/main/res/layout/fragment_signin.xml
+++ /dev/null
@@ -1,135 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/navigation/auth_navigation.xml b/app/src/main/res/navigation/auth_navigation.xml
index 2ed512e1..5dbd8d9e 100644
--- a/app/src/main/res/navigation/auth_navigation.xml
+++ b/app/src/main/res/navigation/auth_navigation.xml
@@ -7,7 +7,7 @@
\ No newline at end of file