Aplicacion terminada

This commit is contained in:
2026-07-31 09:51:40 +02:00
parent 2fc7ad3f52
commit 2d185416c6
22 changed files with 1315 additions and 97 deletions
@@ -0,0 +1,120 @@
package com.example.saipp.data
import com.example.saipp.data.model.Assignment
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.sql.SQLException
import java.sql.Types
class AssignmentRepository {
suspend fun getAll(): Result<List<Assignment>> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = """
SELECT a.id_asignacion, a.id_sai, a.id_persona, a.fecha_asignado, a.fecha_devolucion,
s.ns as sai_ns, p.nombre as person_name
FROM Asignado a
LEFT JOIN SAIs s ON a.id_sai = s.id_sai
LEFT JOIN Persona p ON a.id_persona = p.id_persona
""".trimIndent()
val statement = connection.prepareStatement(sql)
val resultSet = statement.executeQuery()
val items = mutableListOf<Assignment>()
while (resultSet.next()) {
items.add(Assignment(
id = resultSet.getInt("id_asignacion"),
idSai = resultSet.getInt("id_sai"),
idPersona = resultSet.getInt("id_persona"),
assignedDate = resultSet.getString("fecha_asignado"),
returnDate = resultSet.getString("fecha_devolucion"),
saiNs = resultSet.getString("sai_ns"),
personName = resultSet.getString("person_name")
))
}
Result.success(items)
} catch (e: SQLException) {
Result.failure(e)
} finally {
connection.close()
}
}
suspend fun getById(id: Int): Result<Assignment> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "SELECT id_asignacion, id_sai, id_persona, fecha_asignado, fecha_devolucion FROM Asignado WHERE id_asignacion = ?"
val statement = connection.prepareStatement(sql)
statement.setInt(1, id)
val resultSet = statement.executeQuery()
if (resultSet.next()) {
Result.success(Assignment(
id = resultSet.getInt("id_asignacion"),
idSai = resultSet.getInt("id_sai"),
idPersona = resultSet.getInt("id_persona"),
assignedDate = resultSet.getString("fecha_asignado"),
returnDate = resultSet.getString("fecha_devolucion")
))
} else {
Result.failure(Exception("not_found"))
}
} catch (e: SQLException) {
Result.failure(e)
} finally {
connection.close()
}
}
suspend fun insert(item: Assignment): Result<Unit> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "INSERT INTO Asignado (id_sai, id_persona, fecha_asignado, fecha_devolucion) VALUES (?, ?, ?, ?)"
val statement = connection.prepareStatement(sql)
statement.setInt(1, item.idSai)
statement.setInt(2, item.idPersona)
statement.setString(3, item.assignedDate)
statement.setString(4, item.returnDate)
if (statement.executeUpdate() > 0) Result.success(Unit)
else Result.failure(Exception("no_rows_inserted"))
} catch (e: SQLException) {
Result.failure(e)
} finally {
connection.close()
}
}
suspend fun update(item: Assignment): Result<Unit> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "UPDATE Asignado SET id_sai = ?, id_persona = ?, fecha_asignado = ?, fecha_devolucion = ? WHERE id_asignacion = ?"
val statement = connection.prepareStatement(sql)
statement.setInt(1, item.idSai)
statement.setInt(2, item.idPersona)
statement.setString(3, item.assignedDate)
statement.setString(4, item.returnDate)
statement.setInt(5, item.id)
if (statement.executeUpdate() > 0) Result.success(Unit)
else Result.failure(Exception("update_failed"))
} catch (e: SQLException) {
Result.failure(e)
} finally {
connection.close()
}
}
suspend fun delete(id: Int): Result<Unit> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "DELETE FROM Asignado WHERE id_asignacion = ?"
val statement = connection.prepareStatement(sql)
statement.setInt(1, id)
if (statement.executeUpdate() > 0) Result.success(Unit)
else Result.failure(Exception("delete_failed"))
} catch (e: SQLException) {
Result.failure(e)
} finally {
connection.close()
}
}
}
@@ -4,20 +4,22 @@ import com.example.saipp.data.model.Battery
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.sql.SQLException
import java.sql.Types
class BatteryRepository {
suspend fun getAll(): Result<List<Battery>> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "SELECT id_bateria, modelo, estado FROM baterias"
val sql = "SELECT id_bateria, id_sai, marca, fecha_instalacion FROM Bateria"
val statement = connection.prepareStatement(sql)
val resultSet = statement.executeQuery()
val items = mutableListOf<Battery>()
while (resultSet.next()) {
items.add(Battery(
id = resultSet.getInt("id_bateria"),
model = resultSet.getString("modelo"),
status = resultSet.getString("estado")
idSai = resultSet.getInt("id_sai").takeIf { !resultSet.wasNull() },
brand = resultSet.getString("marca"),
installDate = resultSet.getString("fecha_instalacion")
))
}
Result.success(items)
@@ -31,10 +33,12 @@ class BatteryRepository {
suspend fun insert(item: Battery): Result<Unit> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "INSERT INTO baterias (modelo, estado) VALUES (?, ?)"
val sql = "INSERT INTO Bateria (id_sai, marca, fecha_instalacion) VALUES (?, ?, ?)"
val statement = connection.prepareStatement(sql)
statement.setString(1, item.model)
statement.setString(2, item.status)
if (item.idSai != null) statement.setInt(1, item.idSai) else statement.setNull(1, Types.INTEGER)
statement.setString(2, item.brand)
statement.setString(3, item.installDate)
if (statement.executeUpdate() > 0) Result.success(Unit)
else Result.failure(Exception("no_rows_inserted"))
} catch (e: SQLException) {
@@ -47,11 +51,13 @@ class BatteryRepository {
suspend fun update(item: Battery): Result<Unit> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "UPDATE baterias SET modelo = ?, estado = ? WHERE id_bateria = ?"
val sql = "UPDATE Bateria SET id_sai = ?, marca = ?, fecha_instalacion = ? WHERE id_bateria = ?"
val statement = connection.prepareStatement(sql)
statement.setString(1, item.model)
statement.setString(2, item.status)
statement.setInt(3, item.id)
if (item.idSai != null) statement.setInt(1, item.idSai) else statement.setNull(1, Types.INTEGER)
statement.setString(2, item.brand)
statement.setString(3, item.installDate)
statement.setInt(4, item.id)
if (statement.executeUpdate() > 0) Result.success(Unit)
else Result.failure(Exception("update_failed"))
} catch (e: SQLException) {
@@ -64,7 +70,7 @@ class BatteryRepository {
suspend fun delete(id: Int): Result<Unit> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "DELETE FROM baterias WHERE id_bateria = ?"
val sql = "DELETE FROM Bateria WHERE id_bateria = ?"
val statement = connection.prepareStatement(sql)
statement.setInt(1, id)
if (statement.executeUpdate() > 0) Result.success(Unit)
@@ -75,4 +81,28 @@ class BatteryRepository {
connection.close()
}
}
suspend fun getById(id: Int): Result<Battery> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "SELECT id_bateria, id_sai, marca, fecha_instalacion FROM Bateria WHERE id_bateria = ?"
val statement = connection.prepareStatement(sql)
statement.setInt(1, id)
val resultSet = statement.executeQuery()
if (resultSet.next()) {
Result.success(Battery(
id = resultSet.getInt("id_bateria"),
idSai = resultSet.getInt("id_sai").takeIf { !resultSet.wasNull() },
brand = resultSet.getString("marca"),
installDate = resultSet.getString("fecha_instalacion")
))
} else {
Result.failure(Exception("not_found"))
}
} catch (e: SQLException) {
Result.failure(e)
} finally {
connection.close()
}
}
}
@@ -9,15 +9,14 @@ class PersonRepository {
suspend fun getAll(): Result<List<Person>> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "SELECT id_persona, nombre, puesto FROM personas"
val sql = "SELECT id_persona, nombre FROM Persona"
val statement = connection.prepareStatement(sql)
val resultSet = statement.executeQuery()
val items = mutableListOf<Person>()
while (resultSet.next()) {
items.add(Person(
id = resultSet.getInt("id_persona"),
name = resultSet.getString("nombre"),
position = resultSet.getString("puesto")
name = resultSet.getString("nombre")
))
}
Result.success(items)
@@ -31,10 +30,9 @@ class PersonRepository {
suspend fun insert(item: Person): Result<Unit> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "INSERT INTO personas (nombre, puesto) VALUES (?, ?)"
val sql = "INSERT INTO Persona (nombre) VALUES (?)"
val statement = connection.prepareStatement(sql)
statement.setString(1, item.name)
statement.setString(2, item.position)
if (statement.executeUpdate() > 0) Result.success(Unit)
else Result.failure(Exception("no_rows_inserted"))
} catch (e: SQLException) {
@@ -47,11 +45,10 @@ class PersonRepository {
suspend fun update(item: Person): Result<Unit> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "UPDATE personas SET nombre = ?, puesto = ? WHERE id_persona = ?"
val sql = "UPDATE Persona SET nombre = ? WHERE id_persona = ?"
val statement = connection.prepareStatement(sql)
statement.setString(1, item.name)
statement.setString(2, item.position)
statement.setInt(3, item.id)
statement.setInt(2, item.id)
if (statement.executeUpdate() > 0) Result.success(Unit)
else Result.failure(Exception("update_failed"))
} catch (e: SQLException) {
@@ -64,7 +61,7 @@ class PersonRepository {
suspend fun delete(id: Int): Result<Unit> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "DELETE FROM personas WHERE id_persona = ?"
val sql = "DELETE FROM Persona WHERE id_persona = ?"
val statement = connection.prepareStatement(sql)
statement.setInt(1, id)
if (statement.executeUpdate() > 0) Result.success(Unit)
@@ -75,4 +72,26 @@ class PersonRepository {
connection.close()
}
}
suspend fun getById(id: Int): Result<Person> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "SELECT id_persona, nombre FROM Persona WHERE id_persona = ?"
val statement = connection.prepareStatement(sql)
statement.setInt(1, id)
val resultSet = statement.executeQuery()
if (resultSet.next()) {
Result.success(Person(
id = resultSet.getInt("id_persona"),
name = resultSet.getString("nombre")
))
} else {
Result.failure(Exception("not_found"))
}
} catch (e: SQLException) {
Result.failure(e)
} finally {
connection.close()
}
}
}
@@ -74,4 +74,23 @@ class SaiRepository {
connection.close()
}
}
suspend fun getById(id: Int): Result<Sai> = withContext(Dispatchers.IO) {
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
try {
val sql = "SELECT id_sai, ns FROM SAIs WHERE id_sai = ?"
val statement = connection.prepareStatement(sql)
statement.setInt(1, id)
val resultSet = statement.executeQuery()
if (resultSet.next()) {
Result.success(Sai(id = resultSet.getInt("id_sai"), ns = resultSet.getString("ns")))
} else {
Result.failure(Exception("not_found"))
}
} catch (e: SQLException) {
Result.failure(e)
} finally {
connection.close()
}
}
}
@@ -0,0 +1,12 @@
package com.example.saipp.data.model
data class Assignment(
val id: Int = 0,
val idSai: Int,
val idPersona: Int,
val assignedDate: String? = null,
val returnDate: String? = null,
// Optional display fields for joining data
val saiNs: String? = null,
val personName: String? = null
)
@@ -2,6 +2,7 @@ package com.example.saipp.data.model
data class Battery(
val id: Int = 0,
val model: String,
val status: String
val idSai: Int? = null,
val brand: String? = null,
val installDate: String? = null
)
@@ -2,6 +2,5 @@ package com.example.saipp.data.model
data class Person(
val id: Int = 0,
val name: String,
val position: String
val name: String
)
@@ -1,19 +1,35 @@
package com.example.saipp.ui
import androidx.compose.runtime.Composable
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.example.saipp.ui.management.*
sealed class Screen(val route: String) {
object Home : Screen("home")
object SaiList : Screen("sai_list")
object SaiScanner : Screen("sai_scanner")
object EditSai : Screen("edit_sai/{saiId}") {
fun createRoute(id: Int) = "edit_sai/$id"
}
object BatteryList : Screen("battery_list")
object AddBattery : Screen("add_battery")
object EditBattery : Screen("edit_battery/{batteryId}") {
fun createRoute(id: Int) = "edit_battery/$id"
}
object PersonList : Screen("person_list")
object AddPerson : Screen("add_person")
object EditPerson : Screen("edit_person/{personId}") {
fun createRoute(id: Int) = "edit_person/$id"
}
object AssignmentList : Screen("assignment_list")
object AddAssignment : Screen("add_assignment")
object EditAssignment : Screen("edit_assignment/{assignmentId}") {
fun createRoute(id: Int) = "edit_assignment/$id"
}
}
@Composable
@@ -25,13 +41,17 @@ fun AppNavigation() {
HomeScreen(
onNavigateToSais = { navController.navigate(Screen.SaiList.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) }
)
}
// SAIs
composable(Screen.SaiList.route) {
SaiListScreen(
onNavigateBack = { navController.popBackStack() },
onNavigateToScan = { navController.navigate(Screen.SaiScanner.route) }
onNavigateToScan = { navController.navigate(Screen.SaiScanner.route) },
onNavigateToEdit = { id -> navController.navigate(Screen.EditSai.createRoute(id)) }
)
}
composable(Screen.SaiScanner.route) {
@@ -39,27 +59,69 @@ fun AppNavigation() {
onNavigateBack = { navController.popBackStack() }
)
}
composable(
route = Screen.EditSai.route,
arguments = listOf(navArgument("saiId") { type = NavType.IntType })
) { backStackEntry ->
val id = backStackEntry.arguments?.getInt("saiId") ?: 0
EditSaiScreen(saiId = id, onNavigateBack = { navController.popBackStack() })
}
// Batteries
composable(Screen.BatteryList.route) {
BatteryListScreen(
onNavigateBack = { navController.popBackStack() },
onNavigateToAdd = { navController.navigate(Screen.AddBattery.route) }
onNavigateToAdd = { navController.navigate(Screen.AddBattery.route) },
onNavigateToEdit = { id -> navController.navigate(Screen.EditBattery.createRoute(id)) }
)
}
composable(Screen.AddBattery.route) {
AddBatteryScreen(
onNavigateBack = { navController.popBackStack() }
)
AddBatteryScreen(onNavigateBack = { navController.popBackStack() })
}
composable(
route = Screen.EditBattery.route,
arguments = listOf(navArgument("batteryId") { type = NavType.IntType })
) { backStackEntry ->
val id = backStackEntry.arguments?.getInt("batteryId") ?: 0
EditBatteryScreen(batteryId = id, onNavigateBack = { navController.popBackStack() })
}
// People
composable(Screen.PersonList.route) {
PersonListScreen(
onNavigateBack = { navController.popBackStack() },
onNavigateToAdd = { navController.navigate(Screen.AddPerson.route) }
onNavigateToAdd = { navController.navigate(Screen.AddPerson.route) },
onNavigateToEdit = { id -> navController.navigate(Screen.EditPerson.createRoute(id)) }
)
}
composable(Screen.AddPerson.route) {
AddPersonScreen(
onNavigateBack = { navController.popBackStack() }
AddPersonScreen(onNavigateBack = { navController.popBackStack() })
}
composable(
route = Screen.EditPerson.route,
arguments = listOf(navArgument("personId") { type = NavType.IntType })
) { backStackEntry ->
val id = backStackEntry.arguments?.getInt("personId") ?: 0
EditPersonScreen(personId = id, onNavigateBack = { navController.popBackStack() })
}
// Assignments
composable(Screen.AssignmentList.route) {
AssignmentListScreen(
onNavigateBack = { navController.popBackStack() },
onNavigateToAdd = { navController.navigate(Screen.AddAssignment.route) },
onNavigateToEdit = { id -> navController.navigate(Screen.EditAssignment.createRoute(id)) }
)
}
composable(Screen.AddAssignment.route) {
AddAssignmentScreen(onNavigateBack = { navController.popBackStack() })
}
composable(
route = Screen.EditAssignment.route,
arguments = listOf(navArgument("assignmentId") { type = NavType.IntType })
) { backStackEntry ->
val id = backStackEntry.arguments?.getInt("assignmentId") ?: 0
EditAssignmentScreen(assignmentId = id, onNavigateBack = { navController.popBackStack() })
}
}
}
@@ -0,0 +1,179 @@
package com.example.saipp.ui.management
import android.widget.Toast
import androidx.compose.foundation.layout.*
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.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.AssignmentRepository
import com.example.saipp.data.PersonRepository
import com.example.saipp.data.SaiRepository
import com.example.saipp.data.model.Assignment
import com.example.saipp.data.model.Person
import com.example.saipp.data.model.Sai
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AddAssignmentScreen(
onNavigateBack: () -> Unit
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val repository = remember { AssignmentRepository() }
val saiRepository = remember { SaiRepository() }
val personRepository = remember { PersonRepository() }
var selectedSaiId by remember { mutableStateOf<Int?>(null) }
var selectedPersonId by remember { mutableStateOf<Int?>(null) }
var assignedDate by remember { mutableStateOf("") }
var returnDate by remember { mutableStateOf("") }
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
var people by remember { mutableStateOf<List<Person>>(emptyList()) }
var isSaving by remember { mutableStateOf(false) }
var saiExpanded by remember { mutableStateOf(false) }
var personExpanded by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
saiRepository.getAll().onSuccess { sais = it }
personRepository.getAll().onSuccess { people = it }
}
Scaffold(
topBar = {
TopAppBar(
title = { Text("Añadir Asignación") },
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(Icons.Default.ArrowBack, contentDescription = "Atrás")
}
}
)
}
) { padding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(16.dp)
) {
// SAI Selection
ExposedDropdownMenuBox(
expanded = saiExpanded,
onExpandedChange = { saiExpanded = !saiExpanded }
) {
val selectedSai = sais.find { it.id == selectedSaiId }
OutlinedTextField(
value = selectedSai?.ns ?: stringResource(R.string.select_sai),
onValueChange = {},
readOnly = true,
label = { Text(stringResource(R.string.label_sai)) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = saiExpanded) },
modifier = Modifier.menuAnchor().fillMaxWidth()
)
ExposedDropdownMenu(
expanded = saiExpanded,
onDismissRequest = { saiExpanded = false }
) {
sais.forEach { sai ->
DropdownMenuItem(
text = { Text(sai.ns) },
onClick = {
selectedSaiId = sai.id
saiExpanded = false
}
)
}
}
}
Spacer(modifier = Modifier.height(16.dp))
// Person Selection
ExposedDropdownMenuBox(
expanded = personExpanded,
onExpandedChange = { personExpanded = !personExpanded }
) {
val selectedPerson = people.find { it.id == selectedPersonId }
OutlinedTextField(
value = selectedPerson?.name ?: stringResource(R.string.select_person),
onValueChange = {},
readOnly = true,
label = { Text(stringResource(R.string.label_person)) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = personExpanded) },
modifier = Modifier.menuAnchor().fillMaxWidth()
)
ExposedDropdownMenu(
expanded = personExpanded,
onDismissRequest = { personExpanded = false }
) {
people.forEach { person ->
DropdownMenuItem(
text = { Text(person.name) },
onClick = {
selectedPersonId = person.id
personExpanded = false
}
)
}
}
}
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = assignedDate,
onValueChange = { assignedDate = it },
label = { Text(stringResource(R.string.label_assigned_date)) },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = returnDate,
onValueChange = { returnDate = it },
label = { Text(stringResource(R.string.label_return_date)) },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(32.dp))
Button(
onClick = {
if (selectedSaiId != null && selectedPersonId != null) {
isSaving = true
scope.launch {
repository.insert(
Assignment(
idSai = selectedSaiId!!,
idPersona = selectedPersonId!!,
assignedDate = assignedDate.takeIf { it.isNotBlank() },
returnDate = returnDate.takeIf { it.isNotBlank() }
)
).onSuccess {
Toast.makeText(context, "Asignación guardada", Toast.LENGTH_SHORT).show()
onNavigateBack()
}.onFailure {
Toast.makeText(context, "Error: ${it.message}", Toast.LENGTH_SHORT).show()
}
isSaving = false
}
}
},
modifier = Modifier.fillMaxWidth(),
enabled = !isSaving && selectedSaiId != null && selectedPersonId != null
) {
if (isSaving) CircularProgressIndicator(modifier = Modifier.size(24.dp))
else Text("Guardar")
}
}
}
}
@@ -8,9 +8,13 @@ import androidx.compose.material3.*
import androidx.compose.runtime.*
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.BatteryRepository
import com.example.saipp.data.SaiRepository
import com.example.saipp.data.model.Battery
import com.example.saipp.data.model.Sai
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@@ -20,11 +24,20 @@ fun AddBatteryScreen(
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val repository = remember { BatteryRepository() }
val batteryRepository = remember { BatteryRepository() }
val saiRepository = remember { SaiRepository() }
var model by remember { mutableStateOf("") }
var status by remember { mutableStateOf("") }
var brand by remember { mutableStateOf("") }
var installDate by remember { mutableStateOf("") }
var selectedSaiId by remember { mutableStateOf<Int?>(null) }
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
var isSaving by remember { mutableStateOf(false) }
var expanded by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
saiRepository.getAll().onSuccess { sais = it }
}
Scaffold(
topBar = {
@@ -44,33 +57,80 @@ fun AddBatteryScreen(
.padding(padding)
.padding(16.dp)
) {
OutlinedTextField(
value = model,
onValueChange = { model = it },
label = { Text("Modelo") },
modifier = Modifier.fillMaxWidth()
)
// SAI Selection
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = !expanded }
) {
val selectedSai = sais.find { it.id == selectedSaiId }
OutlinedTextField(
value = selectedSai?.ns ?: stringResource(R.string.no_sai),
onValueChange = {},
readOnly = true,
label = { Text(stringResource(R.string.label_sai)) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
modifier = Modifier.menuAnchor().fillMaxWidth()
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
DropdownMenuItem(
text = { Text(stringResource(R.string.no_sai)) },
onClick = {
selectedSaiId = null
expanded = false
}
)
sais.forEach { sai ->
DropdownMenuItem(
text = { Text(sai.ns) },
onClick = {
selectedSaiId = sai.id
expanded = false
}
)
}
}
}
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = status,
onValueChange = { status = it },
label = { Text("Estado") },
value = brand,
onValueChange = { brand = it },
label = { Text(stringResource(R.string.label_brand)) },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = installDate,
onValueChange = { installDate = it },
label = { Text(stringResource(R.string.label_install_date)) },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(32.dp))
Button(
onClick = {
if (model.isNotBlank() && status.isNotBlank()) {
isSaving = true
scope.launch {
repository.insert(Battery(model = model, status = status))
.onSuccess {
Toast.makeText(context, "Batería añadida", Toast.LENGTH_SHORT).show()
onNavigateBack()
}
.onFailure { Toast.makeText(context, "Error: ${it.message}", Toast.LENGTH_SHORT).show() }
isSaving = false
isSaving = true
scope.launch {
batteryRepository.insert(
Battery(
idSai = selectedSaiId,
brand = brand,
installDate = installDate.takeIf { it.isNotBlank() }
)
).onSuccess {
Toast.makeText(context, "Batería añadida", Toast.LENGTH_SHORT).show()
onNavigateBack()
}.onFailure {
Toast.makeText(context, "Error: ${it.message}", Toast.LENGTH_SHORT).show()
}
isSaving = false
}
},
modifier = Modifier.fillMaxWidth(),
@@ -23,7 +23,6 @@ fun AddPersonScreen(
val repository = remember { PersonRepository() }
var name by remember { mutableStateOf("") }
var position by remember { mutableStateOf("") }
var isSaving by remember { mutableStateOf(false) }
Scaffold(
@@ -50,20 +49,13 @@ fun AddPersonScreen(
label = { Text("Nombre") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = position,
onValueChange = { position = it },
label = { Text("Puesto") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(32.dp))
Button(
onClick = {
if (name.isNotBlank() && position.isNotBlank()) {
if (name.isNotBlank()) {
isSaving = true
scope.launch {
repository.insert(Person(name = name, position = position))
repository.insert(Person(name = name))
.onSuccess {
Toast.makeText(context, "Persona añadida", Toast.LENGTH_SHORT).show()
onNavigateBack()
@@ -0,0 +1,108 @@
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.Add
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
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.unit.dp
import com.example.saipp.data.AssignmentRepository
import com.example.saipp.data.model.Assignment
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AssignmentListScreen(
onNavigateBack: () -> Unit,
onNavigateToAdd: () -> Unit,
onNavigateToEdit: (Int) -> Unit
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val repository = remember { AssignmentRepository() }
var items by remember { mutableStateOf<List<Assignment>>(emptyList()) }
var isLoading by remember { mutableStateOf(true) }
fun loadItems() {
scope.launch {
isLoading = true
repository.getAll()
.onSuccess { items = it }
.onFailure { Toast.makeText(context, "Error: ${it.message}", Toast.LENGTH_SHORT).show() }
isLoading = false
}
}
LaunchedEffect(Unit) {
loadItems()
}
Scaffold(
topBar = {
TopAppBar(
title = { Text("Listado de Asignaciones") },
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(Icons.Default.ArrowBack, contentDescription = "Atrás")
}
}
)
},
floatingActionButton = {
FloatingActionButton(onClick = onNavigateToAdd) {
Icon(Icons.Default.Add, contentDescription = "Añadir Asignación")
}
}
) { padding ->
if (isLoading) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CircularProgressIndicator()
}
} else {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(padding)
) {
items(items) { item ->
ListItem(
headlineContent = { Text("${item.personName ?: "Persona desconocida"}") },
supportingContent = {
Column {
Text("SAI: ${item.saiNs ?: "N/A"}")
Text("Asignado: ${item.assignedDate ?: "N/A"}")
if (item.returnDate != null) {
Text("Devolución: ${item.returnDate}")
}
}
},
trailingContent = {
Row {
IconButton(onClick = { onNavigateToEdit(item.id) }) {
Icon(Icons.Default.Edit, contentDescription = "Editar")
}
IconButton(onClick = {
scope.launch {
repository.delete(item.id).onSuccess { loadItems() }
}
}) {
Icon(Icons.Default.Delete, contentDescription = "Eliminar")
}
}
}
)
HorizontalDivider()
}
}
}
}
}
@@ -8,6 +8,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
@@ -22,7 +23,8 @@ import kotlinx.coroutines.launch
@Composable
fun BatteryListScreen(
onNavigateBack: () -> Unit,
onNavigateToAdd: () -> Unit
onNavigateToAdd: () -> Unit,
onNavigateToEdit: (Int) -> Unit
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
@@ -73,15 +75,25 @@ fun BatteryListScreen(
) {
items(items) { item ->
ListItem(
headlineContent = { Text("Modelo: ${item.model}") },
supportingContent = { Text("Estado: ${item.status}") },
headlineContent = { Text("Marca: ${item.brand ?: "Desconocida"}") },
supportingContent = {
Column {
Text("Instalación: ${item.installDate ?: "N/A"}")
item.idSai?.let { Text("SAI ID: $it") }
}
},
trailingContent = {
IconButton(onClick = {
scope.launch {
repository.delete(item.id).onSuccess { loadItems() }
Row {
IconButton(onClick = { onNavigateToEdit(item.id) }) {
Icon(Icons.Default.Edit, contentDescription = "Editar")
}
IconButton(onClick = {
scope.launch {
repository.delete(item.id).onSuccess { loadItems() }
}
}) {
Icon(Icons.Default.Delete, contentDescription = "Eliminar")
}
}) {
Icon(Icons.Default.Delete, contentDescription = "Eliminar")
}
}
)
@@ -0,0 +1,203 @@
package com.example.saipp.ui.management
import android.widget.Toast
import androidx.compose.foundation.layout.*
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.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.AssignmentRepository
import com.example.saipp.data.PersonRepository
import com.example.saipp.data.SaiRepository
import com.example.saipp.data.model.Assignment
import com.example.saipp.data.model.Person
import com.example.saipp.data.model.Sai
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EditAssignmentScreen(
assignmentId: Int,
onNavigateBack: () -> Unit
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val repository = remember { AssignmentRepository() }
val saiRepository = remember { SaiRepository() }
val personRepository = remember { PersonRepository() }
var selectedSaiId by remember { mutableStateOf<Int?>(null) }
var selectedPersonId by remember { mutableStateOf<Int?>(null) }
var assignedDate by remember { mutableStateOf("") }
var returnDate by remember { mutableStateOf("") }
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
var people by remember { mutableStateOf<List<Person>>(emptyList()) }
var isLoading by remember { mutableStateOf(true) }
var isSaving by remember { mutableStateOf(false) }
var saiExpanded by remember { mutableStateOf(false) }
var personExpanded by remember { mutableStateOf(false) }
LaunchedEffect(assignmentId) {
val assignmentResult = repository.getById(assignmentId)
val saisResult = saiRepository.getAll()
val peopleResult = personRepository.getAll()
if (assignmentResult.isSuccess) {
val item = assignmentResult.getOrNull()!!
selectedSaiId = item.idSai
selectedPersonId = item.idPersona
assignedDate = item.assignedDate ?: ""
returnDate = item.returnDate ?: ""
sais = saisResult.getOrDefault(emptyList())
people = peopleResult.getOrDefault(emptyList())
isLoading = false
} else {
Toast.makeText(context, "Error al cargar asignación", Toast.LENGTH_SHORT).show()
onNavigateBack()
}
}
Scaffold(
topBar = {
TopAppBar(
title = { Text("Editar Asignación") },
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(Icons.Default.ArrowBack, contentDescription = "Atrás")
}
}
)
}
) { padding ->
if (isLoading) {
Box(modifier = Modifier.fillMaxSize().padding(padding), contentAlignment = androidx.compose.ui.Alignment.Center) {
CircularProgressIndicator()
}
} else {
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(16.dp)
) {
// SAI Selection
ExposedDropdownMenuBox(
expanded = saiExpanded,
onExpandedChange = { saiExpanded = !saiExpanded }
) {
val selectedSai = sais.find { it.id == selectedSaiId }
OutlinedTextField(
value = selectedSai?.ns ?: stringResource(R.string.select_sai),
onValueChange = {},
readOnly = true,
label = { Text(stringResource(R.string.label_sai)) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = saiExpanded) },
modifier = Modifier.menuAnchor().fillMaxWidth()
)
ExposedDropdownMenu(
expanded = saiExpanded,
onDismissRequest = { saiExpanded = false }
) {
sais.forEach { sai ->
DropdownMenuItem(
text = { Text(sai.ns) },
onClick = {
selectedSaiId = sai.id
saiExpanded = false
}
)
}
}
}
Spacer(modifier = Modifier.height(16.dp))
// Person Selection
ExposedDropdownMenuBox(
expanded = personExpanded,
onExpandedChange = { personExpanded = !personExpanded }
) {
val selectedPerson = people.find { it.id == selectedPersonId }
OutlinedTextField(
value = selectedPerson?.name ?: stringResource(R.string.select_person),
onValueChange = {},
readOnly = true,
label = { Text(stringResource(R.string.label_person)) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = personExpanded) },
modifier = Modifier.menuAnchor().fillMaxWidth()
)
ExposedDropdownMenu(
expanded = personExpanded,
onDismissRequest = { personExpanded = false }
) {
people.forEach { person ->
DropdownMenuItem(
text = { Text(person.name) },
onClick = {
selectedPersonId = person.id
personExpanded = false
}
)
}
}
}
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = assignedDate,
onValueChange = { assignedDate = it },
label = { Text(stringResource(R.string.label_assigned_date)) },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = returnDate,
onValueChange = { returnDate = it },
label = { Text(stringResource(R.string.label_return_date)) },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(32.dp))
Button(
onClick = {
if (selectedSaiId != null && selectedPersonId != null) {
isSaving = true
scope.launch {
repository.update(
Assignment(
id = assignmentId,
idSai = selectedSaiId!!,
idPersona = selectedPersonId!!,
assignedDate = assignedDate.takeIf { it.isNotBlank() },
returnDate = returnDate.takeIf { it.isNotBlank() }
)
).onSuccess {
Toast.makeText(context, "Asignación actualizada", Toast.LENGTH_SHORT).show()
onNavigateBack()
}.onFailure {
Toast.makeText(context, "Error: ${it.message}", Toast.LENGTH_SHORT).show()
}
isSaving = false
}
}
},
modifier = Modifier.fillMaxWidth(),
enabled = !isSaving && selectedSaiId != null && selectedPersonId != null
) {
if (isSaving) CircularProgressIndicator(modifier = Modifier.size(24.dp))
else Text("Guardar Cambios")
}
}
}
}
}
@@ -0,0 +1,166 @@
package com.example.saipp.ui.management
import android.widget.Toast
import androidx.compose.foundation.layout.*
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.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.BatteryRepository
import com.example.saipp.data.SaiRepository
import com.example.saipp.data.model.Battery
import com.example.saipp.data.model.Sai
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EditBatteryScreen(
batteryId: Int,
onNavigateBack: () -> Unit
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val batteryRepository = remember { BatteryRepository() }
val saiRepository = remember { SaiRepository() }
var brand by remember { mutableStateOf("") }
var installDate by remember { mutableStateOf("") }
var selectedSaiId by remember { mutableStateOf<Int?>(null) }
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
var isLoading by remember { mutableStateOf(true) }
var isSaving by remember { mutableStateOf(false) }
var expanded by remember { mutableStateOf(false) }
LaunchedEffect(batteryId) {
val batteryResult = batteryRepository.getById(batteryId)
val saisResult = saiRepository.getAll()
if (batteryResult.isSuccess) {
val battery = batteryResult.getOrNull()!!
brand = battery.brand ?: ""
installDate = battery.installDate ?: ""
selectedSaiId = battery.idSai
sais = saisResult.getOrDefault(emptyList())
isLoading = false
} else {
Toast.makeText(context, "Error al cargar batería", Toast.LENGTH_SHORT).show()
onNavigateBack()
}
}
Scaffold(
topBar = {
TopAppBar(
title = { Text("Editar Batería") },
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(Icons.Default.ArrowBack, contentDescription = "Atrás")
}
}
)
}
) { padding ->
if (isLoading) {
Box(modifier = Modifier.fillMaxSize().padding(padding), contentAlignment = androidx.compose.ui.Alignment.Center) {
CircularProgressIndicator()
}
} else {
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(16.dp)
) {
// SAI Selection
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = !expanded }
) {
val selectedSai = sais.find { it.id == selectedSaiId }
OutlinedTextField(
value = selectedSai?.ns ?: stringResource(R.string.no_sai),
onValueChange = {},
readOnly = true,
label = { Text(stringResource(R.string.label_sai)) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
modifier = Modifier.menuAnchor().fillMaxWidth()
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
DropdownMenuItem(
text = { Text(stringResource(R.string.no_sai)) },
onClick = {
selectedSaiId = null
expanded = false
}
)
sais.forEach { sai ->
DropdownMenuItem(
text = { Text(sai.ns) },
onClick = {
selectedSaiId = sai.id
expanded = false
}
)
}
}
}
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = brand,
onValueChange = { brand = it },
label = { Text(stringResource(R.string.label_brand)) },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = installDate,
onValueChange = { installDate = it },
label = { Text(stringResource(R.string.label_install_date)) },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(32.dp))
Button(
onClick = {
isSaving = true
scope.launch {
batteryRepository.update(
Battery(
id = batteryId,
idSai = selectedSaiId,
brand = brand,
installDate = installDate.takeIf { it.isNotBlank() }
)
).onSuccess {
Toast.makeText(context, "Batería actualizada", Toast.LENGTH_SHORT).show()
onNavigateBack()
}.onFailure {
Toast.makeText(context, "Error: ${it.message}", Toast.LENGTH_SHORT).show()
}
isSaving = false
}
},
modifier = Modifier.fillMaxWidth(),
enabled = !isSaving
) {
if (isSaving) CircularProgressIndicator(modifier = Modifier.size(24.dp))
else Text("Guardar Cambios")
}
}
}
}
}
@@ -0,0 +1,94 @@
package com.example.saipp.ui.management
import android.widget.Toast
import androidx.compose.foundation.layout.*
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.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.example.saipp.data.PersonRepository
import com.example.saipp.data.model.Person
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EditPersonScreen(
personId: Int,
onNavigateBack: () -> Unit
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val repository = remember { PersonRepository() }
var name by remember { mutableStateOf("") }
var isLoading by remember { mutableStateOf(true) }
var isSaving by remember { mutableStateOf(false) }
LaunchedEffect(personId) {
repository.getById(personId).onSuccess {
name = it.name
isLoading = false
}.onFailure {
Toast.makeText(context, "Error al cargar persona", Toast.LENGTH_SHORT).show()
onNavigateBack()
}
}
Scaffold(
topBar = {
TopAppBar(
title = { Text("Editar Persona") },
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(Icons.Default.ArrowBack, contentDescription = "Atrás")
}
}
)
}
) { padding ->
if (isLoading) {
Box(modifier = Modifier.fillMaxSize().padding(padding), contentAlignment = androidx.compose.ui.Alignment.Center) {
CircularProgressIndicator()
}
} else {
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(16.dp)
) {
OutlinedTextField(
value = name,
onValueChange = { name = it },
label = { Text("Nombre") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(32.dp))
Button(
onClick = {
if (name.isNotBlank()) {
isSaving = true
scope.launch {
repository.update(Person(id = personId, name = name))
.onSuccess {
Toast.makeText(context, "Persona actualizada", Toast.LENGTH_SHORT).show()
onNavigateBack()
}
.onFailure { Toast.makeText(context, "Error: ${it.message}", Toast.LENGTH_SHORT).show() }
isSaving = false
}
}
},
modifier = Modifier.fillMaxWidth(),
enabled = !isSaving
) {
if (isSaving) CircularProgressIndicator(modifier = Modifier.size(24.dp))
else Text("Guardar Cambios")
}
}
}
}
}
@@ -0,0 +1,94 @@
package com.example.saipp.ui.management
import android.widget.Toast
import androidx.compose.foundation.layout.*
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.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.example.saipp.data.SaiRepository
import com.example.saipp.data.model.Sai
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EditSaiScreen(
saiId: Int,
onNavigateBack: () -> Unit
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val repository = remember { SaiRepository() }
var ns by remember { mutableStateOf("") }
var isLoading by remember { mutableStateOf(true) }
var isSaving by remember { mutableStateOf(false) }
LaunchedEffect(saiId) {
repository.getById(saiId).onSuccess {
ns = it.ns
isLoading = false
}.onFailure {
Toast.makeText(context, "Error al cargar SAI", Toast.LENGTH_SHORT).show()
onNavigateBack()
}
}
Scaffold(
topBar = {
TopAppBar(
title = { Text("Editar SAI") },
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(Icons.Default.ArrowBack, contentDescription = "Atrás")
}
}
)
}
) { padding ->
if (isLoading) {
Box(modifier = Modifier.fillMaxSize().padding(padding), contentAlignment = androidx.compose.ui.Alignment.Center) {
CircularProgressIndicator()
}
} else {
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(16.dp)
) {
OutlinedTextField(
value = ns,
onValueChange = { ns = it },
label = { Text("Número de Serie") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(32.dp))
Button(
onClick = {
if (ns.isNotBlank()) {
isSaving = true
scope.launch {
repository.update(Sai(id = saiId, ns = ns))
.onSuccess {
Toast.makeText(context, "SAI actualizado", Toast.LENGTH_SHORT).show()
onNavigateBack()
}
.onFailure { Toast.makeText(context, "Error: ${it.message}", Toast.LENGTH_SHORT).show() }
isSaving = false
}
}
},
modifier = Modifier.fillMaxWidth(),
enabled = !isSaving
) {
if (isSaving) CircularProgressIndicator(modifier = Modifier.size(24.dp))
else Text("Guardar Cambios")
}
}
}
}
}
@@ -5,7 +5,6 @@ import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.example.saipp.ui.ConnectionIndicator
@@ -17,7 +16,8 @@ import com.example.saipp.data.MariaDbConnection
fun HomeScreen(
onNavigateToSais: () -> Unit,
onNavigateToBatteries: () -> Unit,
onNavigateToPeople: () -> Unit
onNavigateToPeople: () -> Unit,
onNavigateToAssignments: () -> Unit
) {
var connectionStatus by remember { mutableStateOf<Result<Unit>?>(null) }
var isCheckingConnection by remember { mutableStateOf(true) }
@@ -49,7 +49,7 @@ fun HomeScreen(
Spacer(modifier = Modifier.height(32.dp))
MenuButton(
text = "Gestión de SAIs",
text = stringResource(R.string.menu_sais),
onClick = onNavigateToSais,
modifier = Modifier.fillMaxWidth()
)
@@ -57,7 +57,7 @@ fun HomeScreen(
Spacer(modifier = Modifier.height(16.dp))
MenuButton(
text = "Gestión de Baterías",
text = stringResource(R.string.menu_batteries),
onClick = onNavigateToBatteries,
modifier = Modifier.fillMaxWidth()
)
@@ -65,10 +65,18 @@ fun HomeScreen(
Spacer(modifier = Modifier.height(16.dp))
MenuButton(
text = "Gestión de Personas",
text = stringResource(R.string.menu_people),
onClick = onNavigateToPeople,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
MenuButton(
text = stringResource(R.string.menu_assignments),
onClick = onNavigateToAssignments,
modifier = Modifier.fillMaxWidth()
)
}
}
}
@@ -8,6 +8,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
@@ -22,7 +23,8 @@ import kotlinx.coroutines.launch
@Composable
fun PersonListScreen(
onNavigateBack: () -> Unit,
onNavigateToAdd: () -> Unit
onNavigateToAdd: () -> Unit,
onNavigateToEdit: (Int) -> Unit
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
@@ -74,14 +76,18 @@ fun PersonListScreen(
items(items) { item ->
ListItem(
headlineContent = { Text(item.name) },
supportingContent = { Text(item.position) },
trailingContent = {
IconButton(onClick = {
scope.launch {
repository.delete(item.id).onSuccess { loadItems() }
Row {
IconButton(onClick = { onNavigateToEdit(item.id) }) {
Icon(Icons.Default.Edit, contentDescription = "Editar")
}
IconButton(onClick = {
scope.launch {
repository.delete(item.id).onSuccess { loadItems() }
}
}) {
Icon(Icons.Default.Delete, contentDescription = "Eliminar")
}
}) {
Icon(Icons.Default.Delete, contentDescription = "Eliminar")
}
}
)
@@ -8,6 +8,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
@@ -22,7 +23,8 @@ import kotlinx.coroutines.launch
@Composable
fun SaiListScreen(
onNavigateBack: () -> Unit,
onNavigateToScan: () -> Unit
onNavigateToScan: () -> Unit,
onNavigateToEdit: (Int) -> Unit
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
@@ -75,12 +77,17 @@ fun SaiListScreen(
ListItem(
headlineContent = { Text("NS: ${sai.ns}") },
trailingContent = {
IconButton(onClick = {
scope.launch {
repository.delete(sai.id).onSuccess { loadSais() }
Row {
IconButton(onClick = { onNavigateToEdit(sai.id) }) {
Icon(Icons.Default.Edit, contentDescription = "Editar")
}
IconButton(onClick = {
scope.launch {
repository.delete(sai.id).onSuccess { loadSais() }
}
}) {
Icon(Icons.Default.Delete, contentDescription = "Eliminar")
}
}) {
Icon(Icons.Default.Delete, contentDescription = "Eliminar")
}
}
)
+15
View File
@@ -13,4 +13,19 @@
<string name="db_connection_failed">No se pudo establecer conexión con el servidor</string>
<string name="db_connection_error">Error de conexión a la base de datos</string>
<string name="no_rows_inserted">No se insertaron filas en la base de datos</string>
<string name="label_sai">SAI</string>
<string name="label_brand">Marca</string>
<string name="label_install_date">Fecha Instalación (AAAA-MM-DD HH:MM:SS)</string>
<string name="select_sai">Seleccionar SAI</string>
<string name="no_sai">Ninguno</string>
<string name="menu_sais">Gestión de SAIs</string>
<string name="menu_batteries">Gestión de Baterías</string>
<string name="menu_people">Gestión de Personas</string>
<string name="menu_assignments">Gestión de Asignaciones</string>
<string name="label_person">Persona</string>
<string name="label_assigned_date">Fecha Asignación (AAAA-MM-DD)</string>
<string name="label_return_date">Fecha Devolución (AAAA-MM-DD)</string>
<string name="select_person">Seleccionar Persona</string>
</resources>