diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/ui/login.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/ui/login.kt index 8c705de..51010dd 100644 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/ui/login.kt +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/ui/login.kt @@ -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)) } -} +} \ No newline at end of file diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/viewModel/loginVM.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/viewModel/loginVM.kt index f7eb504..43829fb 100644 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/viewModel/loginVM.kt +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/viewModel/loginVM.kt @@ -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 + } + } }