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