diff --git a/app/src/main/java/in/sminnovations/attendanceapp/AppNavigation.kt b/app/src/main/java/in/sminnovations/attendanceapp/AppNavigation.kt deleted file mode 100644 index a289c2d..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/AppNavigation.kt +++ /dev/null @@ -1,41 +0,0 @@ -package `in`.sminnovations.attendanceapp - -import android.os.Build -import androidx.annotation.RequiresApi -import androidx.compose.runtime.Composable -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.rememberNavController -import `in`.sminnovations.attendanceapp.screens.login.ui.LoginScreen - - -@Composable -fun AppNavigation() { - val navController = rememberNavController() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { - NavigationHost(navController = navController) - } -} - -@RequiresApi(Build.VERSION_CODES.O_MR1) -@Composable -fun NavigationHost(navController: NavHostController) { - NavHost(navController = navController, startDestination = "login") { - composable("login") { - LoginScreen( - loginViewModel = hiltViewModel(), - onLoginSuccess = { - navController.navigate("main") { - popUpTo("login") { inclusive = true } - } - } - ) - } - - composable("main") { - MainScreen(navController) - } - } -} diff --git a/app/src/main/java/in/sminnovations/attendanceapp/data/dataStore/Employee.kt b/app/src/main/java/in/sminnovations/attendanceapp/data/dataStore/Employee.kt deleted file mode 100644 index 7f4aafe..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/data/dataStore/Employee.kt +++ /dev/null @@ -1,14 +0,0 @@ -package `in`.sminnovations.attendanceapp.data.dataStore - -data class Employee( - val androidID: String = "", - val dateOfJoining: String = "", - val dateOfLeaving: String = "", - val designation: String = "Android Developer", - val employeeStatus: Boolean = true, - val id: String = "", - val name: String = "", - val password: String = "", - val photo: String = "", - val dateOfBirth: String = "" -) diff --git a/app/src/main/java/in/sminnovations/attendanceapp/data/dataStore/EmployeePreferencesKeys.kt b/app/src/main/java/in/sminnovations/attendanceapp/data/dataStore/EmployeePreferencesKeys.kt deleted file mode 100644 index 58b513c..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/data/dataStore/EmployeePreferencesKeys.kt +++ /dev/null @@ -1,17 +0,0 @@ -package `in`.sminnovations.attendanceapp.data.dataStore - -import androidx.datastore.preferences.core.booleanPreferencesKey -import androidx.datastore.preferences.core.stringPreferencesKey - -object EmployeePreferencesKeys { - val ANDROID_ID = stringPreferencesKey("android_id") - val DATE_OF_JOINING = stringPreferencesKey("date_of_joining") - val DATE_OF_LEAVING = stringPreferencesKey("date_of_leaving") - val DESIGNATION = stringPreferencesKey("designation") - val EMPLOYEE_STATUS = booleanPreferencesKey("employee_status") - val ID = stringPreferencesKey("id") - val NAME = stringPreferencesKey("name") - val PASSWORD = stringPreferencesKey("password") - val PHOTO = stringPreferencesKey("photo") - val DATE_OF_BIRTH = stringPreferencesKey("date_of_birth") -} \ No newline at end of file diff --git a/app/src/main/java/in/sminnovations/attendanceapp/data/dataStore/EmployeeRepo.kt b/app/src/main/java/in/sminnovations/attendanceapp/data/dataStore/EmployeeRepo.kt deleted file mode 100644 index d9afb3c..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/data/dataStore/EmployeeRepo.kt +++ /dev/null @@ -1,83 +0,0 @@ -package `in`.sminnovations.attendanceapp.data.dataStore - -import android.content.Context -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.emptyPreferences -import androidx.datastore.preferences.preferencesDataStore -import dagger.hilt.android.qualifiers.ApplicationContext -import `in`.sminnovations.attendanceapp.screens.settings.data.EmployeeDataSettings -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.map -import java.io.IOException -import javax.inject.Inject -import javax.inject.Singleton - - -val Context.employeeDataStore by preferencesDataStore("employee_data") - -@Singleton -class EmployeeRepository @Inject constructor( - @ApplicationContext private val context: Context -) { - suspend fun saveEmployeeData(employee: Employee) { - context.employeeDataStore.edit { preferences -> - preferences[EmployeePreferencesKeys.ANDROID_ID] = employee.androidID - preferences[EmployeePreferencesKeys.DATE_OF_JOINING] = employee.dateOfJoining - preferences[EmployeePreferencesKeys.DATE_OF_LEAVING] = employee.dateOfLeaving - preferences[EmployeePreferencesKeys.DESIGNATION] = employee.designation - preferences[EmployeePreferencesKeys.EMPLOYEE_STATUS] = employee.employeeStatus - preferences[EmployeePreferencesKeys.ID] = employee.id - preferences[EmployeePreferencesKeys.NAME] = employee.name - preferences[EmployeePreferencesKeys.PASSWORD] = employee.password - preferences[EmployeePreferencesKeys.PHOTO] = employee.photo - preferences[EmployeePreferencesKeys.DATE_OF_BIRTH] = employee.dateOfBirth - } - } - - - fun getEmployeeData(): Flow { - return context.employeeDataStore.data - .catch { exception -> - // If an error occurs, emit an empty preferences object - if (exception is IOException) { - emit(emptyPreferences()) - } else { - throw exception - } - } - .map { preferences -> - Employee( - androidID = preferences[EmployeePreferencesKeys.ANDROID_ID] ?: "", - dateOfJoining = preferences[EmployeePreferencesKeys.DATE_OF_JOINING] ?: "", - dateOfLeaving = preferences[EmployeePreferencesKeys.DATE_OF_LEAVING] ?: "", - designation = preferences[EmployeePreferencesKeys.DESIGNATION] ?: "Android Developer", - employeeStatus = preferences[EmployeePreferencesKeys.EMPLOYEE_STATUS] ?: true, - id = preferences[EmployeePreferencesKeys.ID] ?: "", - name = preferences[EmployeePreferencesKeys.NAME] ?: "", - password = preferences[EmployeePreferencesKeys.PASSWORD] ?: "", - photo = preferences[EmployeePreferencesKeys.PHOTO] ?: "", - dateOfBirth = preferences[EmployeePreferencesKeys.DATE_OF_BIRTH] ?: "" - ) - } - } - - fun getEmployeeDataSettings(): Flow { - return context.employeeDataStore.data - .catch { exception -> - if (exception is IOException) { - emit(emptyPreferences()) - } else { - throw exception - } - } - .map { preferences -> - EmployeeDataSettings( - name = preferences[EmployeePreferencesKeys.NAME] ?: "", - id = preferences[EmployeePreferencesKeys.ID] ?: "", - designation = preferences[EmployeePreferencesKeys.DESIGNATION] ?: "" - ) - } - } - -} diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/history/components/AttendenceCard.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/history/components/AttendenceCard.kt deleted file mode 100644 index 68cd531..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/history/components/AttendenceCard.kt +++ /dev/null @@ -1,73 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.history.components - -import androidx.compose.animation.animateContentSize -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDropDown -import androidx.compose.material.icons.filled.Check -import androidx.compose.material.icons.filled.Close -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import `in`.sminnovations.attendanceapp.screens.history.data.AttendanceData - - -@Composable -fun EnhancedAttendanceCard(attendance: AttendanceData) { - var isExpanded by remember { mutableStateOf(false) } - - Card( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp, horizontal = 16.dp) - .animateContentSize(), // Animate the size change for a smooth expand/collapse - shape = RoundedCornerShape(12.dp), - elevation = CardDefaults.cardElevation(6.dp) - ) { - Column( - modifier = Modifier - .background(MaterialTheme.colorScheme.primaryContainer) - .padding(16.dp) - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth() - ) { - Text( - text = attendance.date, - fontWeight = FontWeight.Bold, - fontSize = 18.sp, - color = MaterialTheme.colorScheme.onPrimaryContainer, - modifier = Modifier.weight(1f) - ) - IconButton( - onClick = { isExpanded = !isExpanded } - ) { - Icon( - imageVector = Icons.Default.ArrowDropDown, - contentDescription = "Expand or Collapse", - tint = MaterialTheme.colorScheme.onPrimaryContainer - ) - } - } - if (isExpanded) { - Spacer(modifier = Modifier.height(8.dp)) - Text( - text = "Check In: ${attendance.checkInTime ?: "N/A"}", - color = MaterialTheme.colorScheme.onPrimaryContainer - ) - Text( - text = "Check Out: ${attendance.checkOutTime ?: "N/A"}", - color = MaterialTheme.colorScheme.onPrimaryContainer - ) - } - } - } -} diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/history/data/Attendance.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/history/data/Attendance.kt deleted file mode 100644 index 7c2f07f..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/history/data/Attendance.kt +++ /dev/null @@ -1,9 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.history.data - -import com.google.firebase.Timestamp - -data class AttendanceData( - val date: String, - val checkInTime: String?, - val checkOutTime: String? -) diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/history/ui/HistoryScreen.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/history/ui/HistoryScreen.kt deleted file mode 100644 index 43c904d..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/history/ui/HistoryScreen.kt +++ /dev/null @@ -1,62 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.history.ui - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material3.Button -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import `in`.sminnovations.attendanceapp.screens.history.components.EnhancedAttendanceCard -import `in`.sminnovations.attendanceapp.screens.history.viewModel.HistoryViewModel -import `in`.sminnovations.attendanceapp.screens.login.viewModel.LoginViewModel - - -@Composable -fun HistoryScreen( - historyViewModel: HistoryViewModel = hiltViewModel(), -) { - val attendanceData = historyViewModel.listOfAttendanceDates.collectAsState().value - Column(modifier = Modifier.fillMaxSize()) { - Row ( - Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ){ - Text( - text = "Your Attendance History", - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Bold, - modifier = Modifier - .padding(16.dp), - color = MaterialTheme.colorScheme.primary - ) - Button(onClick = {historyViewModel.refresh() }) { - Text(text = "Refresh") - } - } - - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(vertical = 8.dp) - ) { - items(attendanceData) { attendance -> - EnhancedAttendanceCard(attendance = attendance) - } - } - } -} - - diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/history/viewModel/historyVM.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/history/viewModel/historyVM.kt deleted file mode 100644 index d37a97e..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/history/viewModel/historyVM.kt +++ /dev/null @@ -1,97 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.history.viewModel - -import android.util.Log -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.google.firebase.Timestamp -import com.google.firebase.firestore.FirebaseFirestore -import com.google.firebase.firestore.toObject -import dagger.hilt.android.lifecycle.HiltViewModel -import `in`.sminnovations.attendanceapp.screens.history.data.AttendanceData -import `in`.sminnovations.attendanceapp.screens.login.data.LoginDataStore -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.launch -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale -import javax.inject.Inject - -@HiltViewModel -class HistoryViewModel @Inject constructor( - private val loginDataStore: LoginDataStore, - private val firebaseFirestore: FirebaseFirestore, -) : ViewModel() { - - private val _isLoggedIn = MutableStateFlow(false) - val isLoggedIn = _isLoggedIn.asStateFlow() - - - private val _listOfAttendanceDates = MutableStateFlow>(emptyList()) - val listOfAttendanceDates = _listOfAttendanceDates.asStateFlow() - - - init { - viewModelScope.launch { - getAttendanceData() - } - } - - - fun refresh() { - viewModelScope.launch { - getAttendanceData() - } - } - - - - private fun getAttendanceData() { - viewModelScope.launch { - loginDataStore.loginId.collect { id -> - firebaseFirestore.collection("attendance_$id").get() - .addOnSuccessListener { result -> - viewModelScope.launch { - val attendanceList = result.documents.mapNotNull { document -> - val date = document.id // Assuming the document ID is the date - - val checkInTimestamp = document.getString("checkIn") ?: "NA" - val checkOutTimestamp = document.getString("checkOut") ?: "NA" - - val checkIn = formatTimestamp(checkInTimestamp) - val checkOut = formatTimestamp(checkOutTimestamp) - - AttendanceData(date, checkIn, checkOut) - } - _listOfAttendanceDates.value = attendanceList - } - } - .addOnFailureListener { exception -> - Log.e("Firestore", "Error getting documents: ", exception) - viewModelScope.launch { - _listOfAttendanceDates.value = emptyList() - } - } - } - } - } - - - private fun formatTimestamp(timestamp: Any): String { - val sdf = SimpleDateFormat("dd MMMM yyyy hh:mm a", Locale.getDefault()) - return if (timestamp is Timestamp) { - sdf.format(timestamp.toDate()) - } else { - timestamp.toString() - } - } - - - - - - - -} - - diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/data/CheckInCheckOutRepo.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/data/CheckInCheckOutRepo.kt deleted file mode 100644 index 405e80b..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/data/CheckInCheckOutRepo.kt +++ /dev/null @@ -1,74 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.home.data - -import android.content.Context -import androidx.datastore.preferences.core.booleanPreferencesKey -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.stringPreferencesKey -import androidx.datastore.preferences.preferencesDataStore -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import javax.inject.Inject -import javax.inject.Singleton - -private const val DATASTORE_NAME = "check_in_check_out_prefs" -private val Context.dataStore by preferencesDataStore(name = DATASTORE_NAME) - -@Singleton -class CheckInCheckOutDataStore @Inject constructor(@ApplicationContext context: Context) { - - private val dataStore = context.dataStore - - private val CHECK_IN_KEY = stringPreferencesKey("check_in_id") - private val CHECK_OUT_KEY = stringPreferencesKey("check_out_id") - private val CHECK_IN_Date = stringPreferencesKey("check_in_date_id") - private val IS_CHECK_IN = booleanPreferencesKey("is_checked_in_id") - private val IS_CHECK_OUT = booleanPreferencesKey("is_checked_in_id") - - - val checkInId: Flow = dataStore.data.map { preferences -> - preferences[CHECK_IN_KEY] ?: "" - } - - val checkOutId: Flow = dataStore.data.map { preferences -> - preferences[CHECK_OUT_KEY] ?: "" - } - - val checkInDate: Flow = dataStore.data.map { preferences -> - preferences[CHECK_IN_Date] ?: "" - } - - - - suspend fun saveLoginData(checkOut: String) { - dataStore.edit { preferences -> - preferences[CHECK_OUT_KEY] = checkOut - } - } - - - suspend fun saveLoginDate(checkDate: String,checkInTime: String) { - dataStore.edit { preferences -> - preferences[CHECK_IN_Date] = checkDate - preferences[CHECK_IN_KEY] = checkInTime - } - } - - suspend fun saveIsLogIn(isCheckIn: Boolean) { - dataStore.edit { preferences -> - preferences[IS_CHECK_IN] = isCheckIn - } - } - - suspend fun clearLoginData() { - dataStore.edit { preferences -> - preferences.remove(CHECK_IN_Date) - preferences.remove(IS_CHECK_OUT) - preferences.remove(IS_CHECK_IN) - preferences.remove(CHECK_IN_KEY) - preferences.remove(CHECK_OUT_KEY) - } - } - - -} diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/Card.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/Card.kt deleted file mode 100644 index 77c11d8..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/Card.kt +++ /dev/null @@ -1,2 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.home.ui.componets - diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/QRscreen.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/QRscreen.kt deleted file mode 100644 index a989a43..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/QRscreen.kt +++ /dev/null @@ -1,37 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.home.ui.componets - -import android.widget.Toast -import androidx.compose.foundation.layout.* -import androidx.compose.material3.Button -import androidx.compose.material3.Text -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.unit.dp -import com.google.accompanist.permissions.ExperimentalPermissionsApi -import com.google.accompanist.permissions.PermissionState -import com.google.accompanist.permissions.isGranted -import com.google.accompanist.permissions.shouldShowRationale - - -@Composable -fun DefaultScreen( - qrCodeValue: String?, -) { - Column( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - qrCodeValue?.let { - Text(text = "Scanned Value: $it", modifier = Modifier.padding(16.dp)) - } - - } -} - - - diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/home.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/home.kt index 6037bd5..3b36423 100644 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/home.kt +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/home.kt @@ -8,14 +8,17 @@ import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically 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.Row import androidx.compose.foundation.layout.Spacer 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.wrapContentSize import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material3.CircularProgressIndicator @@ -26,6 +29,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -40,7 +44,6 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState -import `in`.sminnovations.attendanceapp.screens.home.ui.componets.DefaultScreen import `in`.sminnovations.attendanceapp.screens.home.ui.componets.LargeButton import `in`.sminnovations.attendanceapp.screens.home.ui.componets.QRCodeScannerScreen import `in`.sminnovations.attendanceapp.screens.home.ui.componets.RequestPermissions @@ -51,19 +54,18 @@ import `in`.sminnovations.attendanceapp.screens.home.viewModel.HomeViewModel fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) { val isScanning by viewModel.isScanning.collectAsState() val qrCodeValue by viewModel.qrCodeValue.collectAsState() - val checkInTime by viewModel.checkedIn.collectAsState() - val checkOutTime by viewModel.checkedOut.collectAsState() + val userName by viewModel.userName.collectAsState() + val isLoading by viewModel.isLoading.collectAsState() val isCheckedIn by viewModel.isCheckedIn.collectAsState() val isCheckedOut by viewModel.isCheckedOut.collectAsState() - val isLoading by viewModel.isLoading.collectAsState() val errorMessage by viewModel.errorMessage.collectAsState() val cameraPermissionState = rememberPermissionState(Manifest.permission.CAMERA) - val locationPermissionState = rememberPermissionState(Manifest.permission.ACCESS_FINE_LOCATION) + val allPermissionsGranted = remember { derivedStateOf { - cameraPermissionState.status.isGranted && locationPermissionState.status.isGranted + cameraPermissionState.status.isGranted } } @@ -71,11 +73,8 @@ fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) { if (!allPermissionsGranted.value) { RequestPermissions( cameraPermissionState = cameraPermissionState, - locationPermissionState = locationPermissionState ) } else { - DefaultScreen(qrCodeValue = qrCodeValue) - AnimatedVisibility( visible = isScanning, enter = expandVertically(expandFrom = Alignment.Bottom) + fadeIn(), @@ -93,8 +92,80 @@ fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) { } } } + + AnimatedVisibility( + visible = !isScanning && qrCodeValue == "" && isCheckedIn && !isCheckedOut, + enter = expandVertically(expandFrom = Alignment.Bottom) + fadeIn(), + exit = shrinkVertically(shrinkTowards = Alignment.Bottom) + fadeOut() + ) { + Box( + contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize() + ) { + Column { + OutlinedCard { + Text( + text = "Checked In as", + letterSpacing = 2.sp, + lineHeight = 40.sp, + fontSize = 30.sp, + color = Color.Green, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(10.dp, 5.dp) + ) + } + Spacer(modifier = Modifier.height(10.dp)) + Text( + text = " $userName", + letterSpacing = 2.sp, + lineHeight = 40.sp, + fontSize = 30.sp, + fontWeight = FontWeight.Bold + ) + + } + + + } + + } + + AnimatedVisibility( + visible = !isScanning && qrCodeValue == "" && !isCheckedIn && isCheckedOut, + enter = expandVertically(expandFrom = Alignment.Bottom) + fadeIn(), + exit = shrinkVertically(shrinkTowards = Alignment.Bottom) + fadeOut() + ) { + Box( + contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize() + ) { + Column { + OutlinedCard { + Text( + text = "Checked Out as", + letterSpacing = 2.sp, + lineHeight = 40.sp, + fontSize = 30.sp, + color = Color.Green, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(10.dp, 5.dp) + ) + } + Spacer(modifier = Modifier.height(10.dp)) + Text( + text = " $userName", + letterSpacing = 2.sp, + lineHeight = 40.sp, + fontSize = 30.sp, + fontWeight = FontWeight.Bold + ) + } + + } + + + } } + // Loading Indicator if (isLoading) { CircularProgressIndicator( @@ -102,42 +173,22 @@ fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) { ) } - // Display Error Message - errorMessage?.let { message -> - Box( - modifier = Modifier - .fillMaxWidth() - .align(Alignment.BottomCenter) - .padding(16.dp) - ) { - Text( - text = message, - color = MaterialTheme.colorScheme.error, - fontWeight = FontWeight.Bold, - modifier = Modifier - .background(Color(0xFFFFCDD2)) // Light red background for error - .padding(8.dp) - .align(Alignment.Center) - ) - } - } - OutlinedCard( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(.47f) - .align(Alignment.BottomCenter) - .padding(10.dp), - onClick = { } - ) { + + OutlinedCard(modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(.4f) + .align(Alignment.BottomCenter) + .padding(10.dp), onClick = { }) { Box(modifier = Modifier.fillMaxSize()) { - Column( - modifier = Modifier - .padding(20.dp) - .align(Alignment.TopCenter) - .fillMaxWidth() - ) { - errorMessage?.let { message -> + + errorMessage?.let { message -> + OutlinedCard( + modifier = Modifier + .wrapContentSize() + .align(Alignment.TopCenter) + .padding(16.dp) + ) { Text( text = message, color = MaterialTheme.colorScheme.error, @@ -145,67 +196,32 @@ fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) { modifier = Modifier .background(Color(0xFFFFCDD2)) // Light red background for error .padding(8.dp) - ) } - AnimatedVisibility(visible = isCheckedIn && !isCheckedOut) { - Text( - text = "You already checked in. Scanning again will Check out.", - letterSpacing = 2.sp, - lineHeight = 40.sp, - fontSize = 30.sp, - color = MaterialTheme.colorScheme.error, - fontWeight = FontWeight.Bold - ) - } - AnimatedVisibility(visible = !isCheckedIn && !isCheckedOut) { - Text( - text = "Please Check In", - letterSpacing = 2.sp, - lineHeight = 40.sp, - fontSize = 30.sp, - color = Color.Green, - fontWeight = FontWeight.Bold - ) - } - Column(Modifier.padding(15.dp)) { - AnimatedVisibility(visible = isCheckedIn || isCheckedOut) { - Text(text = "Check-in: $checkInTime") - Spacer(modifier = Modifier.height(10.dp)) - } - AnimatedVisibility(visible = (isCheckedIn && isCheckedOut) || isCheckedOut ) { - Text(text = "Check-out: $checkOutTime") - Spacer(modifier = Modifier.height(10.dp)) - } - } } + Column(Modifier.align(Alignment.BottomCenter)) { - LargeButton( - text = if (isScanning) "Close" else "Scan", - onClick = if (isScanning) { - { viewModel.stopScanning() } - } else { - { viewModel.startScanning() } - }, - modifier = Modifier.padding(bottom = 24.dp) - ) - if (isScanning) { - BackHandler { viewModel.stopScanning() } + Row( + Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + LargeButton(text = if (isScanning) "Close Scanner" else "Open Scanner", + onClick = if (isScanning) { + { + viewModel.stopScanning() + } + } else { + { + viewModel.startScanning() + + } + }, + modifier = Modifier.padding(bottom = 24.dp)) } - AnimatedVisibility(visible = !isCheckedIn) { - LargeButton( - text = "Check In", - onClick = { viewModel.loginNow() }, - modifier = Modifier.padding(bottom = 24.dp) - ) - } - AnimatedVisibility(visible = isCheckedIn ) { - LargeButton( - text = "Check Out", - onClick = { viewModel.loginNow() }, - modifier = Modifier.padding(bottom = 24.dp) - ) + LaunchedEffect(qrCodeValue) { + viewModel.login() } if (isScanning) { BackHandler { viewModel.stopScanning() } diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/viewModel/homeVM.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/viewModel/homeVM.kt index c563b13..92a9a72 100644 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/viewModel/homeVM.kt +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/viewModel/homeVM.kt @@ -4,25 +4,21 @@ package `in`.sminnovations.attendanceapp.screens.home.viewModel import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.compose.viewModel import com.google.firebase.Timestamp import com.google.firebase.firestore.FirebaseFirestore import dagger.hilt.android.lifecycle.HiltViewModel -import `in`.sminnovations.attendanceapp.screens.home.data.CheckInCheckOutDataStore -import `in`.sminnovations.attendanceapp.screens.login.data.LoginDataStore import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import java.text.SimpleDateFormat -import java.time.LocalDate import java.util.Date import java.util.Locale import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( - private val loginDataStore: LoginDataStore, - private val checkInCheckOutDataStore: CheckInCheckOutDataStore, private val firebaseFirestore: FirebaseFirestore, ) : ViewModel() { @@ -32,11 +28,9 @@ class HomeViewModel @Inject constructor( private val _isScanning = MutableStateFlow(false) val isScanning: StateFlow = _isScanning.asStateFlow() - private val _checkedIn = MutableStateFlow("") - val checkedIn: StateFlow = _checkedIn.asStateFlow() + private val _userName = MutableStateFlow(null) + val userName: StateFlow = _userName.asStateFlow() - private val _checkedOut = MutableStateFlow("N/A") - val checkedOut: StateFlow = _checkedOut.asStateFlow() private val _isCheckedIn = MutableStateFlow(false) val isCheckedIn: StateFlow = _isCheckedIn.asStateFlow() @@ -50,6 +44,7 @@ class HomeViewModel @Inject constructor( private val _isLoading = MutableStateFlow(false) val isLoading: StateFlow = _isLoading.asStateFlow() + private fun postErrorMessage(message: String) { viewModelScope.launch { _errorMessage.emit(message) @@ -58,10 +53,17 @@ class HomeViewModel @Inject constructor( fun startScanning() { _isScanning.value = true + _errorMessage.value = null + _isCheckedIn.value = false + _isCheckedOut.value = false + _userName.value ="" } fun stopScanning() { _isScanning.value = false + _errorMessage.value = null + _isCheckedIn.value = false + _isCheckedOut.value = false } fun setQRCodeValue(value: String) { @@ -79,175 +81,159 @@ class HomeViewModel @Inject constructor( init { viewModelScope.launch { - getCheckInChekout() + startScanning() } } - private fun getCheckInChekout() { - viewModelScope.launch { - checkInCheckOutDataStore.checkInId.collect { checkIn -> - _checkedIn.value = checkIn - } - } - viewModelScope.launch { - checkInCheckOutDataStore.checkOutId.collect { checkOut -> - _checkedOut.value = checkOut - } - } - - viewModelScope.launch { - checkInCheckOutDataStore.checkInDate.collect {date -> - if (date == LocalDate.now().toString()) { - _isCheckedIn.value = true - } - } - - } - - - } - - - private fun getFormattedDate(): String { - val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()) - return dateFormat.format(Date()) - } - - fun loginNow() { + private fun putCheckInAttendance(id: String) { viewModelScope.launch { _isLoading.value = true - checkInCheckOutDataStore.checkInDate.collect { checkOutDate -> - loginDataStore.loginId.collect { _ -> - firebaseFirestore.collection("qr_key").whereEqualTo("id", getFormattedDate()) - .get().addOnSuccessListener { querySnapshot -> - _isLoading.value = false - if (!querySnapshot.isEmpty) { - for (document in querySnapshot) { - if (qrCodeValue.value == document.getString("key")) { - viewModelScope.launch { - if (checkOutDate != LocalDate.now().toString() || checkOutDate == "" - ) { - putCheckInAttendance("") - } else { - viewModelScope.launch { - _isCheckedIn.value = true - } + val currentDate = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(Date()) + val attendanceRef = firebaseFirestore.collection("attendance_$id").document(currentDate) - putCheckOutAttendance() - } - } - } else { - postErrorMessage("Invalid QR code scanned") - } - } - } else { - postErrorMessage("No matching QR key found for today's date") - } - }.addOnFailureListener { exception -> - _isLoading.value = false - postErrorMessage("Error fetching QR key: ${exception.message}") - } - } - } - } - } + attendanceRef.get().addOnSuccessListener { document -> + _isLoading.value = false + if (!document.exists()) { + val checkInData = hashMapOf( + "DayCompleted" to false, + "checkIn" to formatTimestamp(Timestamp.now()), + "checkOut" to "NA" + ) - - private fun putCheckInAttendance(location: String) { - viewModelScope.launch { - _isLoading.value = true - loginDataStore.loginId.collect { id -> - val currentDate = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(Date()) - val attendanceRef = - firebaseFirestore.collection("attendance_$id").document(currentDate) - - attendanceRef.get().addOnSuccessListener { document -> - _isLoading.value = false - if (!document.exists()) { - val checkInData = hashMapOf( - "Status" to false, - "checkIn" to formatTimestamp(Timestamp.now()), - "location" to location, - "checkOut" to "NA" - ) - - attendanceRef.set(checkInData).addOnSuccessListener { - viewModelScope.launch { - checkInCheckOutDataStore.saveLoginDate( - checkDate = LocalDate.now().toString(), - checkInTime = formatTimestamp(Timestamp.now()) - ) - } - Log.d("Firestore", "Check-in successful") - viewModelScope.launch { - _isCheckedIn.value = true - } - }.addOnFailureListener { e -> - postErrorMessage("Error adding check-in: ${e.message}") - } - } else { - postErrorMessage("Check-in already exists for today. Cannot check in again.") + attendanceRef.set(checkInData).addOnSuccessListener { + Log.d("Firestore", "Check-in successful") viewModelScope.launch { - _isCheckedIn.value = true + _isCheckedIn.value =true } + }.addOnFailureListener { e -> + postErrorMessage("Error adding check-in: ${e.message}") } - }.addOnFailureListener { exception -> - _isLoading.value = false - postErrorMessage("Error fetching document: ${exception.message}") + } else { + putCheckOutAttendance(id) } + }.addOnFailureListener { exception -> + _isLoading.value = false + postErrorMessage("Error fetching document: ${exception.message}") } } } - private fun putCheckOutAttendance() { + private fun putCheckOutAttendance(id: String) { viewModelScope.launch { _isLoading.value = true - loginDataStore.loginId.collect { id -> - val currentDate = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(Date()) - val attendanceRef = - firebaseFirestore.collection("attendance_$id").document(currentDate) + val currentDate = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(Date()) + val attendanceRef = firebaseFirestore.collection("attendance_$id").document(currentDate) - attendanceRef.get().addOnSuccessListener { document -> - _isLoading.value = false - if (document.exists()) { - val checkIn = document.getString("checkIn") - val checkOut = document.getString("checkOut") + attendanceRef.get().addOnSuccessListener { document -> + _isLoading.value = false + if (document.exists()) { + val checkIn = document.getString("checkIn") + val checkOut = document.getString("checkOut") - if (checkIn != null && checkOut == "NA") { - val checkOutData = hashMapOf( - "checkOut" to formatTimestamp(Timestamp.now()), "Status" to true - ) as Map + if (checkIn != null && checkOut == "NA") { + val checkOutData = hashMapOf( + "checkOut" to formatTimestamp(Timestamp.now()), "DayCompleted" to true + ) as Map - attendanceRef.update(checkOutData).addOnSuccessListener { - viewModelScope.launch { - checkInCheckOutDataStore.saveLoginData( - checkOut = formatTimestamp( - Timestamp.now() - ) - ) - checkInCheckOutDataStore.saveIsLogIn(false) - } - Log.d("Firestore", "Check-out successful") - }.addOnFailureListener { e -> - postErrorMessage("Error adding check-out: ${e.message}") - } - } else if (checkOut != "NA") { - postErrorMessage("Already checked out for today.") + attendanceRef.update(checkOutData).addOnSuccessListener { viewModelScope.launch { - _isCheckedOut.value = true + _isCheckedOut.value =true + } + Log.d("Firestore", "Check-out successful") + }.addOnFailureListener { e -> + postErrorMessage("Error adding check-out: ${e.message}") + } + } else if (checkOut != "NA") { + postErrorMessage("Already checked out for today.") + } else { + putCheckInAttendance(id) + } + } else { + + postErrorMessage("No attendance found for today. Please check in first.") + } + }.addOnFailureListener { exception -> + _isLoading.value = false + postErrorMessage("Error fetching document: ${exception.message}") + } + + } + } + + fun login(userId: String? = qrCodeValue.value) { + if (qrCodeValue.value != "" && userId != null) { // don't simplify it + _isLoading.value = true + viewModelScope.launch { + _errorMessage.emit(null) + firebaseFirestore.collection("employees").whereEqualTo("Id", userId).get() + .addOnSuccessListener { + if (!it.isEmpty) { + for (doc in it) { + if (true == doc.getBoolean("EmployeeStatus")) { + viewModelScope.launch { + _isLoading.value = false + _userName.value = doc.getString("Name") + putQRvalueEmpty() + putCheckInAttendance(userId) + + } + } else { + postErrorMessage("ID has been expired }") + } } } else { - postErrorMessage("Cannot check out without checking in.") + _isLoading.value = false + postErrorMessage("Error fetching ID }") + + } - } else { - postErrorMessage("No attendance found for today. Please check in first.") + }.addOnFailureListener { + _isLoading.value = false + postErrorMessage("Error FailureListener}") } - }.addOnFailureListener { exception -> - _isLoading.value = false - postErrorMessage("Error fetching document: ${exception.message}") - } } + } else { + + } + } + + fun logout(userId: String? = qrCodeValue.value) { + if (qrCodeValue.value != "" && userId != null) { + viewModelScope.launch { + _errorMessage.emit(null) + firebaseFirestore.collection("employees").whereEqualTo("Id", userId).get() + .addOnSuccessListener { + if (!it.isEmpty) { + for (doc in it) { + if (true == doc.getBoolean("EmployeeStatus")) { + viewModelScope.launch { + _userName.value =doc.getString("Name") + putQRvalueEmpty() + putCheckOutAttendance(userId) + } + } else { + _isLoading.value = false + postErrorMessage("ID has been expired }") + } + } + } else { + _isLoading.value = false + postErrorMessage("Error fetching ID }") + } + }.addOnFailureListener { + _isLoading.value = false + postErrorMessage("Error FailureListener}") + } + } + } else { + + } + } + + private fun putQRvalueEmpty() { + viewModelScope.launch { + _qrCodeValue.value = "" } } diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/LoginDataStore.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/LoginDataStore.kt deleted file mode 100644 index d8bcc4c..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/LoginDataStore.kt +++ /dev/null @@ -1,54 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.login.data -// File: LoginDataStore.kt - -import android.content.Context -import androidx.datastore.preferences.core.booleanPreferencesKey -import androidx.datastore.preferences.core.stringPreferencesKey -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.preferencesDataStore -import dagger.hilt.android.qualifiers.ApplicationContext -import javax.inject.Inject -import javax.inject.Singleton -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map - -private const val DATASTORE_NAME = "user_prefs" -private val Context.dataStore by preferencesDataStore(name = DATASTORE_NAME) - -@Singleton -class LoginDataStore @Inject constructor(@ApplicationContext context: Context) { - - private val dataStore = context.dataStore - - private val LOGIN_ID_KEY = stringPreferencesKey("login_id") - private val PASSWORD_KEY = stringPreferencesKey("password") - private val IS_LOGGED_IN_KEY = booleanPreferencesKey("is_logged_in") - - val loginId: Flow = dataStore.data.map { preferences -> - preferences[LOGIN_ID_KEY] ?: "" - } - - val password: Flow = dataStore.data.map { preferences -> - preferences[PASSWORD_KEY] ?: "" - } - - val isLoggedIn: Flow = dataStore.data.map { preferences -> - preferences[IS_LOGGED_IN_KEY] ?: false - } - - suspend fun saveLoginData(id: String, password: String) { - dataStore.edit { preferences -> - preferences[LOGIN_ID_KEY] = id - preferences[PASSWORD_KEY] = password - preferences[IS_LOGGED_IN_KEY] = true - } - } - - suspend fun clearLoginData() { - dataStore.edit { preferences -> - preferences[LOGIN_ID_KEY] = "" - preferences[PASSWORD_KEY] = "" - preferences[IS_LOGGED_IN_KEY] = false - } - } -} diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/loginData.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/loginData.kt deleted file mode 100644 index 8fa54fa..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/loginData.kt +++ /dev/null @@ -1,6 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.login.data - -data class LoginData( - val id : String ="", - val password: String = "" -) diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/ui/componets/OutlinedTextFiledWithErrors.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/ui/componets/OutlinedTextFiledWithErrors.kt deleted file mode 100644 index c46dcee..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/ui/componets/OutlinedTextFiledWithErrors.kt +++ /dev/null @@ -1,55 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.login.ui.componets - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.OutlinedTextFieldDefaults -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.input.VisualTransformation -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp - -@Composable -fun OutlinedTextFieldWithError( - value: String, - onValueChange: (String) -> Unit, - label: String, - isError: Boolean, - errorMessage: String, - modifier: Modifier = Modifier, - leadingIcon: @Composable (() -> Unit)? = null, - trailingIcon: @Composable (() -> Unit)? = null, - visualTransformation: VisualTransformation = VisualTransformation.None -) { - Column(modifier = modifier) { - OutlinedTextField( - value = value, - onValueChange = onValueChange, - label = { Text(label) }, - isError = isError, - leadingIcon = leadingIcon, - trailingIcon = trailingIcon, - visualTransformation = visualTransformation, - modifier = Modifier.fillMaxWidth(), - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = if (isError) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary, - unfocusedBorderColor = if (isError) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f), - focusedLabelColor = if (isError) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary, - unfocusedLabelColor = if (isError) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurfaceVariant - ) - ) - if (isError) { - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - style = TextStyle(fontSize = 12.sp), - modifier = Modifier.padding(start = 16.dp, top = 4.dp) - ) - } - } -} \ No newline at end of file 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 deleted file mode 100644 index 51010dd..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/ui/login.kt +++ /dev/null @@ -1,155 +0,0 @@ -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 -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.text.input.VisualTransformation -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 isPasswordError by loginViewModel.isPasswordError.collectAsState() - val isUserIdError by loginViewModel.isIdError.collectAsState() - val isLoggedIn by loginViewModel.isLoggedIn.collectAsState() - - var userId by remember { mutableStateOf("") } - var password by remember { mutableStateOf("") } - var passwordVisible by remember { mutableStateOf(false) } - - LaunchedEffect(isLoggedIn) { - if (isLoggedIn) onLoginSuccess() - } - - Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .padding(horizontal = 16.dp, vertical = 24.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text(text = "Attendance App", style = MaterialTheme.typography.headlineLarge) - Spacer(modifier = Modifier.height(40.dp)) - - Image( - painter = painterResource(id = R.drawable.smi_logo), - contentDescription = "App Logo", - modifier = Modifier - .fillMaxWidth(0.5f) - .aspectRatio(1f) - .padding(8.dp) - ) - - Spacer(modifier = Modifier.height(40.dp)) - Text(text = "Credentials", style = MaterialTheme.typography.headlineMedium) - - Spacer(modifier = Modifier.weight(1f)) - - OutlinedTextFieldWithError( - value = userId, - onValueChange = { userId = it }, - label = "User ID", - isError = isUserIdError, - errorMessage = "Please enter a valid User ID", - modifier = Modifier.fillMaxWidth(), - leadingIcon = { - Icon( - imageVector = Icons.Default.Person, - contentDescription = "User Icon" - ) - } - ) - - 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)) - - Button( - onClick = { - coroutineScope.launch { - loginViewModel.login(userId, password, onLoginSuccess) - } - }, - modifier = Modifier - .fillMaxWidth() - .height(50.dp), - ) { - 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 deleted file mode 100644 index 55e3adf..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/viewModel/loginVM.kt +++ /dev/null @@ -1,126 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.login.viewModel - -import android.util.Log -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.data.dataStore.EmployeeRepository -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.tasks.await -import javax.inject.Inject - -@HiltViewModel -class LoginViewModel @Inject constructor( - private val loginDataStore: LoginDataStore, - private val firebaseFirestore: FirebaseFirestore, - private val employeeRepository: EmployeeRepository -) : ViewModel() { - - 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() - - init { - viewModelScope.launch { - collectLoginData() - } - } - - - fun collectLoginData(){ - viewModelScope.launch { - loginDataStore.isLoggedIn.collect { loggedIn -> - _isLoggedIn.value = loggedIn - if (loggedIn) { - loginDataStore.loginId.collect { id -> - loginDataStore.password.collect { password -> - _loginData.value = LoginData(id, password) - } - } - } - } - } - } - - fun login(userId: String, password: String, onLoginSuccess: () -> Unit) { - viewModelScope.launch { - firebaseFirestore.collection("employees").whereEqualTo("Id", userId).get() - .addOnSuccessListener { - if (!it.isEmpty) { - _isIDError.value = false - for (doc in it) { - if (password == doc.getString("Password")) { - if (true == doc.getBoolean("EmployeeStatus")) { - viewModelScope.launch { - loginDataStore.saveLoginData(userId, password) - _isLoggedIn.value = true - } - viewModelScope.launch { - fetchEmployeeData(userId) - onLoginSuccess() - } - }else{ - _isIDError.value = true - } - } else { - _isPasswordError.value = true - } - } - } else { - _isIDError.value = true - } - }.addOnFailureListener { - _isIDError.value = true - - } - } - } - - - private fun DocumentSnapshot.toEmployee(): Employee { - return Employee( - androidID = getString("AndroidID") ?: "", - dateOfJoining = getString("DateOfJoining") ?: "", - dateOfLeaving = getString("DateOfLeaving") ?: "", - designation = getString("Designation") ?: "", - employeeStatus = getBoolean("EmployeeStatus") ?: true, - id = getString("Id") ?: "", - name = getString("Name") ?: "", - password = getString("Password") ?: "", - photo = getString("Photo") ?: "", - dateOfBirth = getString("dateOfBirth") ?: "" - ) - } - - private suspend fun fetchEmployeeData(employeeId: String) { - val db = firebaseFirestore - val document = db.collection("employees").document(employeeId).get().await() - - if (document.exists()) { - val savedata = document.toEmployee() - Log.d("FetchSuccess", "Fetched Employee Data: $savedata") - employeeRepository.saveEmployeeData(savedata) - } else { - Log.d("FetchError", "Document does not exist for employee ID: $employeeId") - } - } - - - -} diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/qrcode/ui/qrcode.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/qrcode/ui/qrcode.kt deleted file mode 100644 index c9be341..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/qrcode/ui/qrcode.kt +++ /dev/null @@ -1,2 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.qrcode.ui - diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/settings/data/EmployeeData.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/settings/data/EmployeeData.kt deleted file mode 100644 index d15282b..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/settings/data/EmployeeData.kt +++ /dev/null @@ -1,7 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.settings.data - -class EmployeeDataSettings ( - val name : String = "", - val id : String = "", - val designation : String = "" -) \ No newline at end of file diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/settings/viewModel/SettingsViewModel.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/settings/viewModel/SettingsViewModel.kt deleted file mode 100644 index b8f32a6..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/settings/viewModel/SettingsViewModel.kt +++ /dev/null @@ -1,33 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.settings.viewModel - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import dagger.hilt.android.lifecycle.HiltViewModel -import `in`.sminnovations.attendanceapp.data.dataStore.EmployeeRepository -import `in`.sminnovations.attendanceapp.screens.home.data.CheckInCheckOutDataStore -import `in`.sminnovations.attendanceapp.screens.login.data.LoginDataStore -import `in`.sminnovations.attendanceapp.screens.settings.data.EmployeeDataSettings -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class SettingsViewModel @Inject constructor( - private val loginDataStore: LoginDataStore, - private val employeeRepository: EmployeeRepository, - private val checkInCheckOutDataStore: CheckInCheckOutDataStore, -) : ViewModel() { - val employeeDataSettings : Flow = employeeRepository.getEmployeeDataSettings() - fun logout() { - viewModelScope.launch { - loginDataStore.clearLoginData() - checkInCheckOutDataStore.clearLoginData() - } - } - - - - - - -} \ No newline at end of file