diff --git a/.idea/other.xml b/.idea/other.xml
index dba9935..a15677d 100644
--- a/.idea/other.xml
+++ b/.idea/other.xml
@@ -47,6 +47,17 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -69,6 +80,17 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -223,6 +245,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/in/sminnovations/smiadmin/BaseActivity.kt b/app/src/main/java/in/sminnovations/smiadmin/BaseActivity.kt
new file mode 100644
index 0000000..98eac9f
--- /dev/null
+++ b/app/src/main/java/in/sminnovations/smiadmin/BaseActivity.kt
@@ -0,0 +1,81 @@
+package `in`.sminnovations.smiadmin
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkRequest
+import android.os.Build
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import `in`.sminnovations.smiadmin.ui.utils.NetworkUtils
+
+abstract class BaseActivity : AppCompatActivity() {
+ private var internetDialog: AlertDialog? = null
+ private lateinit var networkUtils: NetworkUtils
+ private lateinit var connectivityCallback: ConnectivityManager.NetworkCallback
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ networkUtils = NetworkUtils(this)
+ setupNetworkCallback()
+ }
+
+ private fun setupNetworkCallback() {
+ val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+ connectivityCallback = object : ConnectivityManager.NetworkCallback() {
+ override fun onAvailable(network: Network) {
+ runOnUiThread {
+ internetDialog?.dismiss()
+ }
+ }
+
+ override fun onLost(network: Network) {
+ runOnUiThread {
+ showNoInternetDialog()
+ }
+ }
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ connectivityManager.registerDefaultNetworkCallback(connectivityCallback)
+ } else {
+ val request = NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build()
+ connectivityManager.registerNetworkCallback(request, connectivityCallback)
+ }
+ }
+
+ private fun showNoInternetDialog() {
+ if (internetDialog?.isShowing == true) return
+
+ internetDialog = AlertDialog.Builder(this)
+ .setTitle("No Internet Connection")
+ .setMessage("Please turn on your mobile data or connect to Wi-Fi")
+ .setCancelable(false)
+ .setPositiveButton("Open Settings") { _, _ ->
+ startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
+ }
+ .create()
+
+ internetDialog?.show()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ if (!networkUtils.isInternetAvailable()) {
+ showNoInternetDialog()
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+ connectivityManager.unregisterNetworkCallback(connectivityCallback)
+ internetDialog?.dismiss()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/in/sminnovations/smiadmin/MainActivity.kt b/app/src/main/java/in/sminnovations/smiadmin/MainActivity.kt
index 81ca4e8..0ae23ce 100644
--- a/app/src/main/java/in/sminnovations/smiadmin/MainActivity.kt
+++ b/app/src/main/java/in/sminnovations/smiadmin/MainActivity.kt
@@ -13,7 +13,7 @@ import androidx.navigation.ui.setupWithNavController
import com.google.android.material.bottomnavigation.BottomNavigationView
import `in`.sminnovations.smiadmin.databinding.ActivityMainBinding
-class MainActivity : AppCompatActivity() {
+class MainActivity : BaseActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var navController: NavController
diff --git a/app/src/main/java/in/sminnovations/smiadmin/data/AttendanceData.kt b/app/src/main/java/in/sminnovations/smiadmin/data/AttendanceData.kt
index c62a693..d1b7f52 100644
--- a/app/src/main/java/in/sminnovations/smiadmin/data/AttendanceData.kt
+++ b/app/src/main/java/in/sminnovations/smiadmin/data/AttendanceData.kt
@@ -2,6 +2,7 @@ package `in`.sminnovations.smiadmin.data
data class AttendanceData(
val date: String = "",
- val checkIn: String = "",
- val checkOut: String = ""
+ val checkIns: Map,
+ val checkOuts: Map,
+ var isExpanded: Boolean = false
)
diff --git a/app/src/main/java/in/sminnovations/smiadmin/data/Employee.kt b/app/src/main/java/in/sminnovations/smiadmin/data/Employee.kt
index 83a7f93..d26d7f9 100644
--- a/app/src/main/java/in/sminnovations/smiadmin/data/Employee.kt
+++ b/app/src/main/java/in/sminnovations/smiadmin/data/Employee.kt
@@ -3,7 +3,7 @@ package `in`.sminnovations.smiadmin.data
data class Employee(
var Id: String = "",
var Name: String = "",
- var EmployeeStatus: Boolean = false,
+ var EmployeeStatus: Boolean = true,
var Designation:String = "",
var DateOfJoining: String = "",
var dateOfBirth: String = "",
diff --git a/app/src/main/java/in/sminnovations/smiadmin/ui/adapter/AttendanceAdapter.kt b/app/src/main/java/in/sminnovations/smiadmin/ui/adapter/AttendanceAdapter.kt
index 90ac96a..b5b99c1 100644
--- a/app/src/main/java/in/sminnovations/smiadmin/ui/adapter/AttendanceAdapter.kt
+++ b/app/src/main/java/in/sminnovations/smiadmin/ui/adapter/AttendanceAdapter.kt
@@ -3,7 +3,9 @@ package `in`.sminnovations.smiadmin.ui.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.LinearLayout
import android.widget.TextView
+import androidx.cardview.widget.CardView
import androidx.recyclerview.widget.RecyclerView
import `in`.sminnovations.smiadmin.R
import `in`.sminnovations.smiadmin.data.AttendanceData
@@ -13,8 +15,9 @@ class AttendanceAdapter : RecyclerView.Adapter() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val dateTextView: TextView = itemView.findViewById(R.id.textViewDate)
- val checkInTextView: TextView = itemView.findViewById(R.id.textViewCheckIn)
- val checkOutTextView: TextView = itemView.findViewById(R.id.textViewCheckOut)
+ val checkInsLayout: LinearLayout = itemView.findViewById(R.id.layoutCheckIns)
+ val expandableLayout: LinearLayout = itemView.findViewById(R.id.expandableLayout)
+ val cardView: CardView = itemView.findViewById(R.id.cardView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@@ -26,13 +29,41 @@ class AttendanceAdapter : RecyclerView.Adapter() {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val attendance = attendanceList[position]
holder.dateTextView.text = attendance.date
- holder.checkInTextView.text = "Check In: ${attendance.checkIn}"
- holder.checkOutTextView.text = "Check Out: ${attendance.checkOut}"
+
+ // Clear previous views
+ holder.checkInsLayout.removeAllViews()
+
+ // Create check-in/check-out pairs dynamically
+ val context = holder.itemView.context
+ for (i in 1..attendance.checkIns.size) {
+ val checkInKey = "checkIn$i"
+ val checkOutKey = "checkOut$i"
+
+ if (attendance.checkIns.containsKey(checkInKey)) {
+ val pairLayout = LayoutInflater.from(context)
+ .inflate(R.layout.item_checkin_checkout_pair, holder.checkInsLayout, false)
+
+ val checkInView = pairLayout.findViewById(R.id.textViewCheckIn)
+ val checkOutView = pairLayout.findViewById(R.id.textViewCheckOut)
+
+ checkInView.text = "Check In: ${attendance.checkIns[checkInKey]}"
+ checkOutView.text = "Check Out: ${attendance.checkOuts[checkOutKey] ?: "Pending"}"
+
+ holder.checkInsLayout.addView(pairLayout)
+ }
+ }
+
+ // Set up expansion/collapse
+ holder.expandableLayout.visibility = if (attendance.isExpanded) View.VISIBLE else View.GONE
+
+ holder.cardView.setOnClickListener {
+ attendance.isExpanded = !attendance.isExpanded
+ notifyItemChanged(position)
+ }
}
override fun getItemCount() = attendanceList.size
- // In AttendanceAdapter.kt
fun updateAttendance(newAttendance: List) {
attendanceList.clear()
// Convert date string to comparable format and sort
diff --git a/app/src/main/java/in/sminnovations/smiadmin/ui/fragments/AddEmployeeFragment.kt b/app/src/main/java/in/sminnovations/smiadmin/ui/fragments/AddEmployeeFragment.kt
index 70607a0..da517aa 100644
--- a/app/src/main/java/in/sminnovations/smiadmin/ui/fragments/AddEmployeeFragment.kt
+++ b/app/src/main/java/in/sminnovations/smiadmin/ui/fragments/AddEmployeeFragment.kt
@@ -83,7 +83,7 @@ class AddEmployeeFragment : Fragment() {
}
private fun generateEmployeeId(name: String, phone: String): String {
- val namePrefix = name.take(4).uppercase()
+ val namePrefix = name.take(4).lowercase()
val phoneSuffix = phone.takeLast(4)
return "$namePrefix$phoneSuffix"
}
@@ -136,16 +136,29 @@ class AddEmployeeFragment : Fragment() {
val phone = binding.etEmployeePhone.text.toString().trim()
val employeeId = generateEmployeeId(name, phone)
- val employee = Employee(
- Id = employeeId,
- Name = name,
- Email = binding.etEmployeeEmail.text.toString().trim(),
- Mobile = phone,
- Designation = binding.etEmployeePosition.text.toString().trim(),
- dateOfBirth = dateFormatter.format(dateOfBirth?.time ?: Date()),
- DateOfJoining = dateFormatter.format(dateOfJoining?.time ?: Date()),
- CreatedAt = dateFormatter.format(Date()), // Current date in dd/MM/yyyy format
- from = binding.etEmployeeFrom.text.toString().trim(),
+// val employee = Employee(
+// Id = employeeId,
+// Name = name,
+// Email = binding.etEmployeeEmail.text.toString().trim(),
+// Mobile = phone,
+// Designation = binding.etEmployeePosition.text.toString().trim(),
+// dateOfBirth = dateFormatter.format(dateOfBirth?.time ?: Date()),
+// DateOfJoining = dateFormatter.format(dateOfJoining?.time ?: Date()),
+// CreatedAt = dateFormatter.format(Date()), // Current date in dd/MM/yyyy format
+// from = binding.etEmployeeFrom.text.toString().trim(),
+// )
+ val employee = hashMapOf(
+ "Id" to employeeId,
+ "Name" to name,
+ "EmployeeStatus" to true,
+ "Email" to binding.etEmployeeEmail.text.toString().trim(),
+ "Mobile" to phone,
+ "Designation" to binding.etEmployeePosition.text.toString().trim(),
+ "dateOfBirth" to dateFormatter.format(dateOfBirth?.time ?: Date()),
+ "DateOfJoining" to dateFormatter.format(dateOfJoining?.time ?: Date()),
+ "CreatedAt" to dateFormatter.format(Date()),
+ "from" to binding.etEmployeeFrom.text.toString().trim(),
+ "role" to "user"
)
db.collection("employees")
diff --git a/app/src/main/java/in/sminnovations/smiadmin/ui/fragments/attendance/AttendanceDetailFragment.kt b/app/src/main/java/in/sminnovations/smiadmin/ui/fragments/attendance/AttendanceDetailFragment.kt
index 4cd1fb8..435af74 100644
--- a/app/src/main/java/in/sminnovations/smiadmin/ui/fragments/attendance/AttendanceDetailFragment.kt
+++ b/app/src/main/java/in/sminnovations/smiadmin/ui/fragments/attendance/AttendanceDetailFragment.kt
@@ -2,6 +2,7 @@ package `in`.sminnovations.smiadmin.ui.fragments.attendance
import android.R
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -123,13 +124,38 @@ class AttendanceDetailFragment : Fragment() {
// Only include records for selected month and year
if (docMonth == month && docYear == year) {
- AttendanceData(
- date = doc.id,
- checkIn = doc.getString("checkIn") ?: "N/A",
- checkOut = doc.getString("checkOut") ?: "N/A"
- )
+ // Get all fields from the document
+ val data = doc.data ?: return@mapNotNull null
+
+ // Create maps to store check-ins and check-outs
+ val checkIns = mutableMapOf()
+ val checkOuts = mutableMapOf()
+
+ // Iterate through all fields in the document
+ data.forEach { (key, value) ->
+ when {
+ // Match checkIn1, checkIn2, etc.
+ key.startsWith("checkIn") -> {
+ checkIns[key] = (value as? String) ?: "N/A"
+ }
+ // Match checkOut1, checkOut2, etc.
+ key.startsWith("checkOut") -> {
+ checkOuts[key] = (value as? String) ?: "N/A"
+ }
+ }
+ }
+
+ // Only create AttendanceData if there's at least one check-in
+ if (checkIns.isNotEmpty()) {
+ AttendanceData(
+ date = doc.id,
+ checkIns = checkIns,
+ checkOuts = checkOuts
+ )
+ } else null
} else null
} catch (e: Exception) {
+ Log.e("AttendanceMapping", "Error mapping document: ${doc.id}", e)
null
}
}
diff --git a/app/src/main/java/in/sminnovations/smiadmin/ui/utils/NetworkUtils.kt b/app/src/main/java/in/sminnovations/smiadmin/ui/utils/NetworkUtils.kt
new file mode 100644
index 0000000..cb9fa50
--- /dev/null
+++ b/app/src/main/java/in/sminnovations/smiadmin/ui/utils/NetworkUtils.kt
@@ -0,0 +1,20 @@
+package `in`.sminnovations.smiadmin.ui.utils
+
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.NetworkCapabilities
+import android.os.Build
+
+class NetworkUtils(private val context: Context) {
+ fun isInternetAvailable(): Boolean {
+ val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ val network = connectivityManager.activeNetwork ?: return false
+ val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
+ return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ } else {
+ val networkInfo = connectivityManager.activeNetworkInfo ?: return false
+ return networkInfo.isConnected
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/baseline_arrow_drop_down_24.xml b/app/src/main/res/drawable/baseline_arrow_drop_down_24.xml
new file mode 100644
index 0000000..a77e331
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_arrow_drop_down_24.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_attendance_detail.xml b/app/src/main/res/layout/fragment_attendance_detail.xml
index db08bfb..3fe65ca 100644
--- a/app/src/main/res/layout/fragment_attendance_detail.xml
+++ b/app/src/main/res/layout/fragment_attendance_detail.xml
@@ -18,7 +18,7 @@
android:id="@+id/spinnerMonth"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
+ android:layout_marginStart="5dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textViewEmployeeName" />
@@ -26,7 +26,7 @@
android:id="@+id/spinnerYear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
+ android:layout_marginStart="5dp"
app:layout_constraintStart_toEndOf="@id/spinnerMonth"
app:layout_constraintTop_toTopOf="@id/spinnerMonth" />
diff --git a/app/src/main/res/layout/item_attendance.xml b/app/src/main/res/layout/item_attendance.xml
index 626dbaa..f91915f 100644
--- a/app/src/main/res/layout/item_attendance.xml
+++ b/app/src/main/res/layout/item_attendance.xml
@@ -1,42 +1,71 @@
-
+ app:cardElevation="4dp"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools">
+ android:orientation="vertical">
-
-
-
+
+ android:padding="16dp"
+ android:background="?android:attr/selectableItemBackground">
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:textSize="18sp"
+ android:textStyle="bold"
+ />
-
+
+
+
+
+
+
+
+
+
+ android:orientation="vertical" />
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_checkin_checkout_pair.xml b/app/src/main/res/layout/item_checkin_checkout_pair.xml
new file mode 100644
index 0000000..420993a
--- /dev/null
+++ b/app/src/main/res/layout/item_checkin_checkout_pair.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
\ No newline at end of file