Añadir filtros en listado SAIS y eliminar SAIS Disponibles

This commit is contained in:
2026-08-03 13:31:26 +02:00
parent 18328607d0
commit 7884b7e374
6 changed files with 41 additions and 148 deletions
@@ -36,29 +36,6 @@ class SaiRepository {
} }
} }
suspend fun getAvailable(): Result<List<Sai>> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "SELECT id_sai, ns, modelo, estado FROM SAIs WHERE estado = 'Disponible'"
val statement = connection.prepareStatement(sql)
val resultSet = statement.executeQuery()
val sais = mutableListOf<Sai>()
while (resultSet.next()) {
sais.add(Sai(
id = resultSet.getInt("id_sai"),
ns = resultSet.getString("ns"),
modelo = resultSet.getString("modelo"),
estado = resultSet.getString("estado")
))
}
Result.success(sais)
} catch (e: SQLException) {
Result.failure(e)
} finally {
connection.close()
}
}
suspend fun isNsRegistered(ns: String): Result<Boolean> = withContext(Dispatchers.IO) { suspend fun isNsRegistered(ns: String): Result<Boolean> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error")) val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try { try {
@@ -33,7 +33,6 @@ sealed class Screen(val route: String) {
object EditAssignment : Screen("edit_assignment/{assignmentId}") { object EditAssignment : Screen("edit_assignment/{assignmentId}") {
fun createRoute(id: Int) = "edit_assignment/$id" fun createRoute(id: Int) = "edit_assignment/$id"
} }
object AvailableSaiList : Screen("available_sai_list")
object QuickInfo : Screen("quick_info") object QuickInfo : Screen("quick_info")
} }
@@ -48,7 +47,6 @@ fun AppNavigation() {
onNavigateToBatteries = { navController.navigate(Screen.BatteryList.route) }, onNavigateToBatteries = { navController.navigate(Screen.BatteryList.route) },
onNavigateToPeople = { navController.navigate(Screen.PersonList.route) }, onNavigateToPeople = { navController.navigate(Screen.PersonList.route) },
onNavigateToAssignments = { navController.navigate(Screen.AssignmentList.route) }, onNavigateToAssignments = { navController.navigate(Screen.AssignmentList.route) },
onNavigateToAvailableSais = { navController.navigate(Screen.AvailableSaiList.route) },
onNavigateToQuickInfo = { navController.navigate(Screen.QuickInfo.route) } onNavigateToQuickInfo = { navController.navigate(Screen.QuickInfo.route) }
) )
} }
@@ -136,9 +134,6 @@ fun AppNavigation() {
val id = backStackEntry.arguments?.getInt("assignmentId") ?: 0 val id = backStackEntry.arguments?.getInt("assignmentId") ?: 0
EditAssignmentScreen(assignmentId = id, onNavigateBack = { navController.popBackStack() }) EditAssignmentScreen(assignmentId = id, onNavigateBack = { navController.popBackStack() })
} }
composable(Screen.AvailableSaiList.route) {
AvailableSaiListScreen(onNavigateBack = { navController.popBackStack() })
}
composable(Screen.QuickInfo.route) { composable(Screen.QuickInfo.route) {
QuickInfoScreen(onNavigateBack = { navController.popBackStack() }) QuickInfoScreen(onNavigateBack = { navController.popBackStack() })
} }
@@ -1,97 +0,0 @@
package com.example.saipp.ui.management
import android.widget.Toast
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.example.saipp.R
import com.example.saipp.data.SaiRepository
import com.example.saipp.data.model.Sai
import com.example.saipp.ui.SearchBar
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AvailableSaiListScreen(
onNavigateBack: () -> Unit
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val repository = remember { SaiRepository() }
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
var isLoading by remember { mutableStateOf(true) }
var searchQuery by remember { mutableStateOf("") }
fun loadSais() {
scope.launch {
isLoading = true
repository.getAvailable()
.onSuccess { sais = it }
.onFailure { Toast.makeText(context, "Error: ${it.message}", Toast.LENGTH_SHORT).show() }
isLoading = false
}
}
LaunchedEffect(Unit) {
loadSais()
}
val filteredSais = remember(sais, searchQuery) {
sais.filter {
it.ns.contains(searchQuery, ignoreCase = true) ||
(it.modelo?.contains(searchQuery, ignoreCase = true) ?: false)
}
}
Scaffold(
topBar = {
TopAppBar(
title = { Text(stringResource(R.string.menu_available_sais)) },
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(Icons.Default.ArrowBack, contentDescription = "Atrás")
}
}
)
}
) { padding ->
Column(modifier = Modifier.fillMaxSize().padding(padding)) {
SearchBar(
query = searchQuery,
onQueryChange = { searchQuery = it },
modifier = Modifier.padding(16.dp)
)
if (isLoading) {
Box(modifier = Modifier.weight(1f).fillMaxWidth(), contentAlignment = Alignment.Center) {
CircularProgressIndicator()
}
} else {
if (filteredSais.isEmpty()) {
Box(modifier = Modifier.weight(1f).fillMaxWidth(), contentAlignment = Alignment.Center) {
Text("No hay SAIs disponibles")
}
} else {
LazyColumn(modifier = Modifier.weight(1f).fillMaxWidth()) {
items(filteredSais) { sai ->
ListItem(
headlineContent = { Text("NS: ${sai.ns}") },
supportingContent = { Text("Modelo: ${sai.modelo ?: "N/A"}") }
)
HorizontalDivider()
}
}
}
}
}
}
}
@@ -23,7 +23,6 @@ fun HomeScreen(
onNavigateToBatteries: () -> Unit, onNavigateToBatteries: () -> Unit,
onNavigateToPeople: () -> Unit, onNavigateToPeople: () -> Unit,
onNavigateToAssignments: () -> Unit, onNavigateToAssignments: () -> Unit,
onNavigateToAvailableSais: () -> Unit,
onNavigateToQuickInfo: () -> Unit onNavigateToQuickInfo: () -> Unit
) { ) {
var connectionStatus by remember { mutableStateOf<Result<Unit>?>(null) } var connectionStatus by remember { mutableStateOf<Result<Unit>?>(null) }
@@ -125,22 +124,6 @@ fun HomeScreen(
} }
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = onNavigateToAvailableSais,
modifier = Modifier.fillMaxWidth().height(56.dp),
shape = MaterialTheme.shapes.medium,
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
)
) {
Icon(Icons.Default.CheckCircle, contentDescription = null)
Spacer(modifier = Modifier.width(8.dp))
Text(text = stringResource(R.string.menu_available_sais), style = MaterialTheme.typography.titleMedium)
}
Spacer(modifier = Modifier.height(16.dp))
} }
} }
} }
@@ -1,9 +1,11 @@
package com.example.saipp.ui.management package com.example.saipp.ui.management
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
import androidx.compose.material3.* import androidx.compose.material3.*
@@ -12,12 +14,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.example.saipp.data.SaiRepository import com.example.saipp.data.SaiRepository
import com.example.saipp.data.model.Sai import com.example.saipp.data.model.Sai
import com.example.saipp.ui.SearchBar import com.example.saipp.ui.SearchBar
import com.example.saipp.ui.ScannerDialog import com.example.saipp.ui.ScannerDialog
import com.example.saipp.ui.DeleteConfirmDialog import com.example.saipp.ui.DeleteConfirmDialog
import com.example.saipp.R
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@@ -36,6 +40,9 @@ fun SaiListScreen(
var searchQuery by remember { mutableStateOf("") } var searchQuery by remember { mutableStateOf("") }
var showQuickScanner by remember { mutableStateOf(false) } var showQuickScanner by remember { mutableStateOf(false) }
var selectedStatusFilter by remember { mutableStateOf("Todos") }
val statusOptions = listOf("Todos", "Disponible", "Asignado", "En reparación", "Baja")
var saiToDelete by remember { mutableStateOf<Sai?>(null) } var saiToDelete by remember { mutableStateOf<Sai?>(null) }
fun loadSais() { fun loadSais() {
@@ -52,10 +59,14 @@ fun SaiListScreen(
loadSais() loadSais()
} }
val filteredSais = remember(sais, searchQuery) { val filteredSais = remember(sais, searchQuery, selectedStatusFilter) {
sais.filter { sais.filter { sai ->
it.ns.contains(searchQuery, ignoreCase = true) || val matchesSearch = sai.ns.contains(searchQuery, ignoreCase = true) ||
(it.modelo?.contains(searchQuery, ignoreCase = true) ?: false) (sai.modelo?.contains(searchQuery, ignoreCase = true) ?: false)
val matchesStatus = if (selectedStatusFilter == "Todos") true else sai.estado == selectedStatusFilter
matchesSearch && matchesStatus
} }
} }
@@ -80,10 +91,34 @@ fun SaiListScreen(
SearchBar( SearchBar(
query = searchQuery, query = searchQuery,
onQueryChange = { searchQuery = it }, onQueryChange = { searchQuery = it },
modifier = Modifier.padding(16.dp), modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
onScanClick = { showQuickScanner = true } onScanClick = { showQuickScanner = true }
) )
// Status Filter Chips
Row(
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState())
.padding(horizontal = 16.dp, vertical = 4.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
statusOptions.forEach { status ->
FilterChip(
selected = selectedStatusFilter == status,
onClick = { selectedStatusFilter = status },
label = {
Text(
if (status == "Todos") stringResource(R.string.filter_all) else status
)
},
leadingIcon = if (selectedStatusFilter == status) {
{ Icon(Icons.Default.Check, contentDescription = null, modifier = Modifier.size(16.dp)) }
} else null
)
}
}
if (showQuickScanner) { if (showQuickScanner) {
ScannerDialog( ScannerDialog(
onDismiss = { showQuickScanner = false }, onDismiss = { showQuickScanner = false },
+1 -1
View File
@@ -30,6 +30,7 @@
<string name="action_deactivate">Dar de Baja</string> <string name="action_deactivate">Dar de Baja</string>
<string name="action_reactivate">Reactivar</string> <string name="action_reactivate">Reactivar</string>
<string name="filter_hide_inactive">Ocultar personal de baja</string> <string name="filter_hide_inactive">Ocultar personal de baja</string>
<string name="filter_all">Todos</string>
<string name="dashboard_title">Estado General</string> <string name="dashboard_title">Estado General</string>
<string name="stats_sais">SAIs</string> <string name="stats_sais">SAIs</string>
@@ -53,7 +54,6 @@
<string name="menu_batteries">Gestión de Baterías</string> <string name="menu_batteries">Gestión de Baterías</string>
<string name="menu_people">Gestión de Personas</string> <string name="menu_people">Gestión de Personas</string>
<string name="menu_assignments">Gestion SAI - Persona</string> <string name="menu_assignments">Gestion SAI - Persona</string>
<string name="menu_available_sais">SAIs Disponibles</string>
<string name="label_person">Persona</string> <string name="label_person">Persona</string>
<string name="label_assigned_date">Fecha Asignación (DD-MM-AAAA)</string> <string name="label_assigned_date">Fecha Asignación (DD-MM-AAAA)</string>