LoginScreen

This commit is contained in:
sathwikcs
2024-10-16 17:38:20 +05:30
parent 6ccab6e184
commit ed354c7799
2 changed files with 130 additions and 68 deletions

View File

@@ -1,14 +1,22 @@
package `in`.sminnovations.attendanceapp.screens.login.ui package `in`.sminnovations.attendanceapp.screens.login.ui
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.Person
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@@ -36,23 +44,23 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import `in`.sminnovations.attendanceapp.R import `in`.sminnovations.attendanceapp.R
import `in`.sminnovations.attendanceapp.screens.login.ui.componets.OutlinedTextFieldWithError
import `in`.sminnovations.attendanceapp.screens.login.viewModel.LoginViewModel import `in`.sminnovations.attendanceapp.screens.login.viewModel.LoginViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
fun LoginScreen( fun LoginScreen(
loginViewModel: LoginViewModel = hiltViewModel(), loginViewModel: LoginViewModel = hiltViewModel(),
onLoginSuccess: () -> Unit onLoginSuccess: () -> Unit
) { ) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val isLoading by loginViewModel.isLoading.collectAsState() val isPasswordError by loginViewModel.isPasswordError.collectAsState()
val isUserIdError by loginViewModel.isIdError.collectAsState()
val isLoggedIn by loginViewModel.isLoggedIn.collectAsState() val isLoggedIn by loginViewModel.isLoggedIn.collectAsState()
var userId by remember { mutableStateOf(TextFieldValue("")) } var userId by remember { mutableStateOf("") }
var password by remember { mutableStateOf(TextFieldValue("")) } var password by remember { mutableStateOf("") }
var passwordVisible by remember { mutableStateOf(false) } var passwordVisible by remember { mutableStateOf(false) }
// Navigate to the main screen if the user is already logged in
LaunchedEffect(isLoggedIn) { LaunchedEffect(isLoggedIn) {
if (isLoggedIn) onLoginSuccess() if (isLoggedIn) onLoginSuccess()
} }
@@ -60,81 +68,88 @@ fun LoginScreen(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(26.dp), .verticalScroll(rememberScrollState())
.padding(horizontal = 16.dp, vertical = 24.dp),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Text(text = "Attendance App", style = MaterialTheme.typography.headlineLarge) Text(text = "Attendance App", style = MaterialTheme.typography.headlineLarge)
Spacer(modifier = Modifier.height(50.dp)) Spacer(modifier = Modifier.height(40.dp))
// Logo Image
Image( Image(
painter = painterResource(id = R.drawable.smi_logo), painter = painterResource(id = R.drawable.smi_logo),
contentDescription = "App Logo", contentDescription = "App Logo",
alignment = Alignment.TopCenter modifier = Modifier
.fillMaxWidth(0.5f)
.aspectRatio(1f)
.padding(8.dp)
) )
Spacer(modifier = Modifier.height(100.dp)) Spacer(modifier = Modifier.height(40.dp))
Text(text = "Credentials", style = MaterialTheme.typography.headlineMedium) Text(text = "Credentials", style = MaterialTheme.typography.headlineMedium)
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.weight(1f))
// User ID TextField OutlinedTextFieldWithError(
OutlinedTextField(
value = userId, value = userId,
onValueChange = { userId = it }, onValueChange = { userId = it },
label = { Text("User ID") }, label = "User ID",
isError = isUserIdError,
errorMessage = "Please enter a valid User ID",
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Next) leadingIcon = {
) Icon(
imageVector = Icons.Default.Person,
Spacer(modifier = Modifier.height(30.dp)) contentDescription = "User Icon"
)
// Password TextField
OutlinedTextField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
modifier = Modifier.fillMaxWidth(),
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done),
trailingIcon = {
val icon = if (passwordVisible) {
painterResource(id = R.drawable.baseline_visibility_24)
} else {
painterResource(id = R.drawable.baseline_visibility_off_24)
}
IconButton(onClick = { passwordVisible = !passwordVisible }) {
Icon(painter = icon, contentDescription = "Toggle Password Visibility")
}
} }
) )
Spacer(modifier = Modifier.height(60.dp)) Spacer(modifier = Modifier.height(16.dp))
OutlinedTextFieldWithError(
value = password,
onValueChange = { password = it },
label = "Password",
isError = isPasswordError,
errorMessage = "Please enter a valid password",
modifier = Modifier.fillMaxWidth(),
leadingIcon = {
Icon(
imageVector = Icons.Default.Lock,
contentDescription = "Password Icon"
)
},
trailingIcon = {
IconButton(onClick = { passwordVisible = !passwordVisible }) {
Icon(
painter = painterResource(
id = if (passwordVisible) R.drawable.baseline_visibility_24 else R.drawable.baseline_visibility_off_24
),
contentDescription = if (passwordVisible) "Hide Password" else "Show Password"
)
}
},
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation()
)
Spacer(modifier = Modifier.height(24.dp))
// Login Button
Button( Button(
onClick = { onClick = {
coroutineScope.launch { coroutineScope.launch {
loginViewModel.login(userId.text, password.text, onLoginSuccess) loginViewModel.login(userId, password, onLoginSuccess)
} }
}, },
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(50.dp), .height(50.dp),
enabled = !isLoading
) { ) {
if (isLoading) { Text(
CircularProgressIndicator( text = "Login",
color = Color.White, fontSize = 20.sp
modifier = Modifier.size(24.dp), )
strokeWidth = 2.dp
)
} else {
Text(
text = "Login",
fontSize = 20.sp
)
}
} }
Spacer(modifier = Modifier.weight(1f))
} }
} }

View File

@@ -1,30 +1,35 @@
package `in`.sminnovations.attendanceapp.screens.login.viewModel package `in`.sminnovations.attendanceapp.screens.login.viewModel
// File: LoginViewModel.kt
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FirebaseFirestore
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import `in`.sminnovations.attendanceapp.data.dataStore.Employee
import `in`.sminnovations.attendanceapp.screens.login.data.LoginData import `in`.sminnovations.attendanceapp.screens.login.data.LoginData
import `in`.sminnovations.attendanceapp.screens.login.data.LoginDataStore import `in`.sminnovations.attendanceapp.screens.login.data.LoginDataStore
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.delay import kotlinx.coroutines.tasks.await
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class LoginViewModel @Inject constructor( class LoginViewModel @Inject constructor(
private val loginDataStore: LoginDataStore private val loginDataStore: LoginDataStore, private val firebaseFirestore: FirebaseFirestore
) : ViewModel() { ) : ViewModel() {
private val _isLoading = MutableStateFlow(false) private val _isPasswordError = MutableStateFlow(false)
val isLoading = _isLoading.asStateFlow() val isPasswordError = _isPasswordError.asStateFlow()
private val _isIDError = MutableStateFlow(false)
val isIdError = _isIDError.asStateFlow()
private val _isLoggedIn = MutableStateFlow(false) private val _isLoggedIn = MutableStateFlow(false)
val isLoggedIn = _isLoggedIn.asStateFlow() val isLoggedIn = _isLoggedIn.asStateFlow()
private val _loginData = MutableStateFlow(LoginData("", "")) private val _loginData = MutableStateFlow(LoginData("", ""))
val loginData = _loginData.asStateFlow() val loginData = _loginData.asStateFlow()
@@ -41,20 +46,35 @@ class LoginViewModel @Inject constructor(
} }
} }
} }
} }
fun login(userId: String, password: String, onLoginSuccess: () -> Unit) { fun login(userId: String, password: String, onLoginSuccess: () -> Unit) {
viewModelScope.launch { viewModelScope.launch {
_isLoading.value = true firebaseFirestore.collection("employees").whereEqualTo("Id", userId).get()
delay(1000) // Simulate network call .addOnSuccessListener {
if (!it.isEmpty) {
_isIDError.value = false
for (doc in it) {
if (password == doc.getString("Password")) {
viewModelScope.launch {
loginDataStore.saveLoginData(userId, password)
_isLoggedIn.value = true
onLoginSuccess()
if (userId == "1" && password == "2") { }
loginDataStore.saveLoginData(userId, password) } else {
_isLoggedIn.value = true _isPasswordError.value = true
onLoginSuccess() }
} }
_isLoading.value = false } else {
_isIDError.value = true
}
}.addOnFailureListener {
_isIDError.value = true
}
} }
} }
fun logout() { fun logout() {
@@ -63,4 +83,31 @@ class LoginViewModel @Inject constructor(
_isLoggedIn.value = false _isLoggedIn.value = false
} }
} }
private fun DocumentSnapshot.toEmployee(): Employee {
return Employee(
androidID = getString("AndroidID") ?: "",
dateOfJoining = getString("DateOfJoining") ?: "",
dateOfLeaving = getString("DateOfLeaving") ?: "",
designation = getString("Designation") ?: "Android Developer",
employeeStatus = getBoolean("EmployeeStatus") ?: true,
id = getString("Id") ?: "",
name = getString("Name") ?: "",
password = getString("Password") ?: "",
photo = getString("Photo") ?: "",
dateOfBirth = getString("dateOfBirth") ?: ""
)
}
suspend fun fetchEmployeeData(employeeId: String): Employee? {
val db = FirebaseFirestore.getInstance()
return try {
val document = db.collection("employees").document(employeeId).get().await()
document.toEmployee()
} catch (e: Exception) {
e.printStackTrace()
null
}
}
} }