LoginScreen
This commit is contained in:
@@ -1,14 +1,22 @@
|
||||
package `in`.sminnovations.attendanceapp.screens.login.ui
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
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.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -36,23 +44,23 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import `in`.sminnovations.attendanceapp.R
|
||||
import `in`.sminnovations.attendanceapp.screens.login.ui.componets.OutlinedTextFieldWithError
|
||||
import `in`.sminnovations.attendanceapp.screens.login.viewModel.LoginViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun LoginScreen(
|
||||
loginViewModel: LoginViewModel = hiltViewModel(),
|
||||
onLoginSuccess: () -> Unit
|
||||
) {
|
||||
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()
|
||||
|
||||
var userId by remember { mutableStateOf(TextFieldValue("")) }
|
||||
var password by remember { mutableStateOf(TextFieldValue("")) }
|
||||
var userId by remember { mutableStateOf("") }
|
||||
var password by remember { mutableStateOf("") }
|
||||
var passwordVisible by remember { mutableStateOf(false) }
|
||||
|
||||
// Navigate to the main screen if the user is already logged in
|
||||
LaunchedEffect(isLoggedIn) {
|
||||
if (isLoggedIn) onLoginSuccess()
|
||||
}
|
||||
@@ -60,81 +68,88 @@ fun LoginScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(26.dp),
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = 16.dp, vertical = 24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(text = "Attendance App", style = MaterialTheme.typography.headlineLarge)
|
||||
Spacer(modifier = Modifier.height(50.dp))
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
|
||||
// Logo Image
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.smi_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)
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
// User ID TextField
|
||||
OutlinedTextField(
|
||||
OutlinedTextFieldWithError(
|
||||
value = userId,
|
||||
onValueChange = { userId = it },
|
||||
label = { Text("User ID") },
|
||||
label = "User ID",
|
||||
isError = isUserIdError,
|
||||
errorMessage = "Please enter a valid User ID",
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Next)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(30.dp))
|
||||
|
||||
// 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")
|
||||
}
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Person,
|
||||
contentDescription = "User Icon"
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
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(
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
loginViewModel.login(userId.text, password.text, onLoginSuccess)
|
||||
loginViewModel.login(userId, password, onLoginSuccess)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(50.dp),
|
||||
enabled = !isLoading
|
||||
) {
|
||||
if (isLoading) {
|
||||
CircularProgressIndicator(
|
||||
color = Color.White,
|
||||
modifier = Modifier.size(24.dp),
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = "Login",
|
||||
fontSize = 20.sp
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = "Login",
|
||||
fontSize = 20.sp
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,35 @@
|
||||
package `in`.sminnovations.attendanceapp.screens.login.viewModel
|
||||
|
||||
// File: LoginViewModel.kt
|
||||
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.google.firebase.firestore.DocumentSnapshot
|
||||
import com.google.firebase.firestore.FirebaseFirestore
|
||||
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.LoginDataStore
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.tasks.await
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class LoginViewModel @Inject constructor(
|
||||
private val loginDataStore: LoginDataStore
|
||||
private val loginDataStore: LoginDataStore, private val firebaseFirestore: FirebaseFirestore
|
||||
) : ViewModel() {
|
||||
|
||||
private val _isLoading = MutableStateFlow(false)
|
||||
val isLoading = _isLoading.asStateFlow()
|
||||
private val _isPasswordError = MutableStateFlow(false)
|
||||
val isPasswordError = _isPasswordError.asStateFlow()
|
||||
|
||||
|
||||
private val _isIDError = MutableStateFlow(false)
|
||||
val isIdError = _isIDError.asStateFlow()
|
||||
|
||||
private val _isLoggedIn = MutableStateFlow(false)
|
||||
val isLoggedIn = _isLoggedIn.asStateFlow()
|
||||
|
||||
|
||||
private val _loginData = MutableStateFlow(LoginData("", ""))
|
||||
val loginData = _loginData.asStateFlow()
|
||||
|
||||
@@ -41,20 +46,35 @@ class LoginViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun login(userId: String, password: String, onLoginSuccess: () -> Unit) {
|
||||
viewModelScope.launch {
|
||||
_isLoading.value = true
|
||||
delay(1000) // Simulate network call
|
||||
firebaseFirestore.collection("employees").whereEqualTo("Id", userId).get()
|
||||
.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)
|
||||
_isLoggedIn.value = true
|
||||
onLoginSuccess()
|
||||
}
|
||||
_isLoading.value = false
|
||||
}
|
||||
} else {
|
||||
_isPasswordError.value = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_isIDError.value = true
|
||||
}
|
||||
}.addOnFailureListener {
|
||||
_isIDError.value = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun logout() {
|
||||
@@ -63,4 +83,31 @@ class LoginViewModel @Inject constructor(
|
||||
_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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user