Changes in logic and backend
This commit is contained in:
@@ -8,15 +8,19 @@ import android.graphics.Paint
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.google.firebase.firestore.FieldValue
|
||||
import com.google.firebase.firestore.FirebaseFirestore
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.journeyapps.barcodescanner.BarcodeEncoder
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import `in`.sminnovations.smi_attendance_admin.screens.home.data.QRCodeDataStore
|
||||
import `in`.sminnovations.smi_attendance_admin.screens.login.data.LoginDataStore
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
@@ -25,13 +29,15 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class HomeViewModel @Inject constructor(
|
||||
private val qrCodeDataStore: QRCodeDataStore, private val firebaseFirestore: FirebaseFirestore
|
||||
private val qrCodeDataStore: QRCodeDataStore,private val loginDataStore: LoginDataStore,private val firebaseFirestore: FirebaseFirestore
|
||||
) : ViewModel() {
|
||||
|
||||
private val _qrCodeBitmap = MutableStateFlow<Bitmap?>(null)
|
||||
val qrCodeBitmap: StateFlow<Bitmap?> = _qrCodeBitmap
|
||||
var isLoading = MutableStateFlow(false)
|
||||
|
||||
fun checkAndGenerateQRCode() {
|
||||
|
||||
val today = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(Date())
|
||||
val documentRef = firebaseFirestore.collection("qr_key").document(today)
|
||||
|
||||
@@ -53,32 +59,44 @@ class HomeViewModel @Inject constructor(
|
||||
// Show a toast from your Composable
|
||||
// Example: Toast.makeText(context, "Document already present: $existingKey", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
// If the document doesn't exist, create it
|
||||
val randomNumber = (1000..9999).random() // Generate a random 4-digit number
|
||||
val newKey = "SMI$randomNumber${SimpleDateFormat("MM", Locale.getDefault()).format(Date())}${SimpleDateFormat("dd", Locale.getDefault()).format(Date())}"
|
||||
viewModelScope.launch {
|
||||
|
||||
val newDocument = hashMapOf(
|
||||
"id" to today,
|
||||
"key" to newKey // Add your key here
|
||||
)
|
||||
val loginId = loginDataStore.getLoginId()
|
||||
// If the document doesn't exist, create it
|
||||
val randomNumber = (1000..9999).random() // Generate a random 4-digit number
|
||||
val newKey = "SMI$randomNumber${
|
||||
SimpleDateFormat(
|
||||
"MM",
|
||||
Locale.getDefault()
|
||||
).format(Date())
|
||||
}${SimpleDateFormat("dd", Locale.getDefault()).format(Date())}"
|
||||
|
||||
// Create the document
|
||||
documentRef.set(newDocument).addOnSuccessListener {
|
||||
// Set the QR code bitmap
|
||||
viewModelScope.launch {
|
||||
val newDocument = hashMapOf(
|
||||
"id" to today,
|
||||
"employeeId" to loginId,
|
||||
"key" to newKey, // Add your key here
|
||||
"createdAt" to FieldValue.serverTimestamp()
|
||||
|
||||
qrCodeDataStore.saveQRData(id = today, key = newKey)
|
||||
)
|
||||
|
||||
_qrCodeBitmap.value = generateQRCode(newKey)
|
||||
// Create the document
|
||||
documentRef.set(newDocument).addOnSuccessListener {
|
||||
// Set the QR code bitmap
|
||||
viewModelScope.launch {
|
||||
|
||||
qrCodeDataStore.saveQRData(id = today, key = newKey)
|
||||
|
||||
_qrCodeBitmap.value = generateQRCode(newKey)
|
||||
|
||||
isLoading.value = false // Stop loading
|
||||
}
|
||||
|
||||
}.addOnFailureListener { e ->
|
||||
// Handle failure
|
||||
isLoading.value = false // Stop loading
|
||||
// Show a toast from your Composable
|
||||
// Example: Toast.makeText(context, "Error creating document: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
}.addOnFailureListener { e ->
|
||||
// Handle failure
|
||||
isLoading.value = false // Stop loading
|
||||
// Show a toast from your Composable
|
||||
// Example: Toast.makeText(context, "Error creating document: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}.addOnFailureListener { e ->
|
||||
|
||||
@@ -10,6 +10,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
private const val DATASTORE_NAME = "user_prefs"
|
||||
@@ -21,12 +22,16 @@ class LoginDataStore @Inject constructor(@ApplicationContext context: Context) {
|
||||
private val dataStore = context.dataStore
|
||||
|
||||
private val LOGIN_ID_KEY = stringPreferencesKey("login_id")
|
||||
private val USER_NAME = stringPreferencesKey("user_name")
|
||||
private val PASSWORD_KEY = stringPreferencesKey("password")
|
||||
private val IS_LOGGED_IN_KEY = booleanPreferencesKey("is_logged_in")
|
||||
|
||||
val loginId: Flow<String> = dataStore.data.map { preferences ->
|
||||
preferences[LOGIN_ID_KEY] ?: ""
|
||||
}
|
||||
val userName: Flow<String> = dataStore.data.map { preferences ->
|
||||
preferences[USER_NAME] ?: ""
|
||||
}
|
||||
|
||||
val password: Flow<String> = dataStore.data.map { preferences ->
|
||||
preferences[PASSWORD_KEY] ?: ""
|
||||
@@ -36,9 +41,10 @@ class LoginDataStore @Inject constructor(@ApplicationContext context: Context) {
|
||||
preferences[IS_LOGGED_IN_KEY] ?: false
|
||||
}
|
||||
|
||||
suspend fun saveLoginData(id: String, password: String) {
|
||||
suspend fun saveLoginData(id: String,name: String, password: String) {
|
||||
dataStore.edit { preferences ->
|
||||
preferences[LOGIN_ID_KEY] = id
|
||||
preferences[USER_NAME] = name
|
||||
preferences[PASSWORD_KEY] = password
|
||||
preferences[IS_LOGGED_IN_KEY] = true
|
||||
}
|
||||
@@ -47,8 +53,13 @@ class LoginDataStore @Inject constructor(@ApplicationContext context: Context) {
|
||||
suspend fun clearLoginData() {
|
||||
dataStore.edit { preferences ->
|
||||
preferences.remove(LOGIN_ID_KEY)
|
||||
preferences.remove(USER_NAME)
|
||||
preferences.remove(PASSWORD_KEY)
|
||||
preferences[IS_LOGGED_IN_KEY] = false
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getLoginId(): String {
|
||||
return dataStore.data.first()[LOGIN_ID_KEY] ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ fun LoginScreen(
|
||||
value = userId,
|
||||
onValueChange = { userId = it },
|
||||
label = "User ID",
|
||||
enabled = !loading,
|
||||
isError = isUserIdError,
|
||||
errorMessage = "Please enter a valid User ID",
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -107,6 +108,7 @@ fun LoginScreen(
|
||||
value = password,
|
||||
onValueChange = { password = it },
|
||||
label = "Password",
|
||||
enabled = !loading,
|
||||
isError = isPasswordError,
|
||||
errorMessage = "Please enter a valid password",
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -130,6 +132,11 @@ fun LoginScreen(
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
LaunchedEffect(isUserIdError, isPasswordError) {
|
||||
if (isUserIdError || isPasswordError) {
|
||||
loading = false // Hide the progress bar if there's an error
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
CircularProgressIndicator(
|
||||
|
||||
@@ -20,6 +20,7 @@ fun OutlinedTextFieldWithError(
|
||||
onValueChange: (String) -> Unit,
|
||||
label: String,
|
||||
isError: Boolean,
|
||||
enabled: Boolean,
|
||||
errorMessage: String,
|
||||
modifier: Modifier = Modifier,
|
||||
leadingIcon: @Composable (() -> Unit)? = null,
|
||||
@@ -32,6 +33,7 @@ fun OutlinedTextFieldWithError(
|
||||
onValueChange = onValueChange,
|
||||
label = { Text(label) },
|
||||
isError = isError,
|
||||
enabled = enabled,
|
||||
leadingIcon = leadingIcon,
|
||||
trailingIcon = trailingIcon,
|
||||
visualTransformation = visualTransformation,
|
||||
|
||||
@@ -57,8 +57,9 @@ class LoginViewModel @Inject constructor(
|
||||
_isIDError.value = false
|
||||
for (doc in it) {
|
||||
if (password == doc.getString("Password")) {
|
||||
val name = doc.getString("Name")!!
|
||||
viewModelScope.launch {
|
||||
loginDataStore.saveLoginData(userId, password)
|
||||
loginDataStore.saveLoginData(userId,name, password)
|
||||
_isLoggedIn.value = true
|
||||
onLoginSuccess()
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@@ -34,6 +36,8 @@ fun TopComponents(
|
||||
mainNavHostController : NavHostController,
|
||||
settingsViewModel : SettingsViewModel
|
||||
) {
|
||||
val loginId by settingsViewModel.loginId.collectAsState()
|
||||
val userName by settingsViewModel.userName.collectAsState()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
@@ -51,7 +55,7 @@ fun TopComponents(
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = "Account Name",
|
||||
text = userName,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 20.sp,
|
||||
@@ -61,20 +65,20 @@ fun TopComponents(
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Text(
|
||||
text = "ID: 69420",
|
||||
text = loginId,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
fontSize = 16.sp
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
Text(
|
||||
text = "Designation",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
fontSize = 16.sp
|
||||
)
|
||||
// Spacer(modifier = Modifier.height(4.dp))
|
||||
//
|
||||
// Text(
|
||||
// text = "Designation",
|
||||
// style = MaterialTheme.typography.bodyMedium,
|
||||
// color = MaterialTheme.colorScheme.primary,
|
||||
// fontSize = 16.sp
|
||||
// )
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
|
||||
@@ -4,9 +4,13 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.google.firebase.firestore.FirebaseFirestore
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import `in`.sminnovations.smi_attendance_admin.screens.home.data.QRCodeDataStore
|
||||
import `in`.sminnovations.smi_attendance_admin.screens.login.data.LoginDataStore
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -15,11 +19,19 @@ class SettingsViewModel @Inject constructor(
|
||||
private val loginDataStore: LoginDataStore,
|
||||
private val firebaseFirestore: FirebaseFirestore
|
||||
) : ViewModel() {
|
||||
|
||||
val loginId: StateFlow<String> = loginDataStore.loginId.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(5000),
|
||||
initialValue = ""
|
||||
)
|
||||
val userName: StateFlow<String> = loginDataStore.userName.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(5000),
|
||||
initialValue = ""
|
||||
)
|
||||
fun logout() {
|
||||
viewModelScope.launch {
|
||||
loginDataStore.clearLoginData()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user