Aplicacion terminada
This commit is contained in:
Generated
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="temurin-21" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ Ensure your MariaDB database has the following tables:
|
|||||||
-- Table for SAIs
|
-- Table for SAIs
|
||||||
CREATE TABLE SAIs (
|
CREATE TABLE SAIs (
|
||||||
id_sai INT AUTO_INCREMENT PRIMARY KEY,
|
id_sai INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
ns VARCHAR(255) NOT NULL
|
ns VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
modelo VARCHAR(255)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Table for Batteries
|
-- Table for Batteries
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
package com.example.saipp.data
|
package com.example.saipp.data
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import com.example.saipp.data.model.Sai
|
import com.example.saipp.data.model.Sai
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.sql.SQLException
|
import java.sql.SQLException
|
||||||
|
|
||||||
class SaiRepository {
|
class SaiRepository {
|
||||||
companion object {
|
|
||||||
private const val TAG = "SaiRepository"
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getAll(): Result<List<Sai>> = withContext(Dispatchers.IO) {
|
suspend fun getAll(): Result<List<Sai>> = 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 {
|
||||||
val sql = "SELECT id_sai, ns FROM SAIs"
|
val sql = "SELECT id_sai, ns, modelo FROM SAIs"
|
||||||
val statement = connection.prepareStatement(sql)
|
val statement = connection.prepareStatement(sql)
|
||||||
val resultSet = statement.executeQuery()
|
val resultSet = statement.executeQuery()
|
||||||
val sais = mutableListOf<Sai>()
|
val sais = mutableListOf<Sai>()
|
||||||
while (resultSet.next()) {
|
while (resultSet.next()) {
|
||||||
sais.add(Sai(id = resultSet.getInt("id_sai"), ns = resultSet.getString("ns")))
|
sais.add(Sai(
|
||||||
|
id = resultSet.getInt("id_sai"),
|
||||||
|
ns = resultSet.getString("ns"),
|
||||||
|
modelo = resultSet.getString("modelo")
|
||||||
|
))
|
||||||
}
|
}
|
||||||
Result.success(sais)
|
Result.success(sais)
|
||||||
} catch (e: SQLException) {
|
} catch (e: SQLException) {
|
||||||
@@ -29,12 +28,32 @@ class SaiRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun insert(ns: String): Result<Unit> = 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 {
|
||||||
val sql = "INSERT INTO SAIs (ns) VALUES (?)"
|
val sql = "SELECT COUNT(*) FROM SAIs WHERE ns = ?"
|
||||||
val statement = connection.prepareStatement(sql)
|
val statement = connection.prepareStatement(sql)
|
||||||
statement.setString(1, ns)
|
statement.setString(1, ns)
|
||||||
|
val resultSet = statement.executeQuery()
|
||||||
|
if (resultSet.next()) {
|
||||||
|
Result.success(resultSet.getInt(1) > 0)
|
||||||
|
} else {
|
||||||
|
Result.success(false)
|
||||||
|
}
|
||||||
|
} catch (e: SQLException) {
|
||||||
|
Result.failure(e)
|
||||||
|
} finally {
|
||||||
|
connection.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun insert(ns: String, modelo: String?): Result<Unit> = withContext(Dispatchers.IO) {
|
||||||
|
val connection = MariaDbConnection.getConnection() ?: return@withContext Result.failure(Exception("db_connection_error"))
|
||||||
|
try {
|
||||||
|
val sql = "INSERT INTO SAIs (ns, modelo) VALUES (?, ?)"
|
||||||
|
val statement = connection.prepareStatement(sql)
|
||||||
|
statement.setString(1, ns)
|
||||||
|
statement.setString(2, modelo)
|
||||||
if (statement.executeUpdate() > 0) Result.success(Unit)
|
if (statement.executeUpdate() > 0) Result.success(Unit)
|
||||||
else Result.failure(Exception("no_rows_inserted"))
|
else Result.failure(Exception("no_rows_inserted"))
|
||||||
} catch (e: SQLException) {
|
} catch (e: SQLException) {
|
||||||
@@ -47,10 +66,11 @@ class SaiRepository {
|
|||||||
suspend fun update(sai: Sai): Result<Unit> = withContext(Dispatchers.IO) {
|
suspend fun update(sai: Sai): Result<Unit> = 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 {
|
||||||
val sql = "UPDATE SAIs SET ns = ? WHERE id_sai = ?"
|
val sql = "UPDATE SAIs SET ns = ?, modelo = ? WHERE id_sai = ?"
|
||||||
val statement = connection.prepareStatement(sql)
|
val statement = connection.prepareStatement(sql)
|
||||||
statement.setString(1, sai.ns)
|
statement.setString(1, sai.ns)
|
||||||
statement.setInt(2, sai.id)
|
statement.setString(2, sai.modelo)
|
||||||
|
statement.setInt(3, sai.id)
|
||||||
if (statement.executeUpdate() > 0) Result.success(Unit)
|
if (statement.executeUpdate() > 0) Result.success(Unit)
|
||||||
else Result.failure(Exception("update_failed"))
|
else Result.failure(Exception("update_failed"))
|
||||||
} catch (e: SQLException) {
|
} catch (e: SQLException) {
|
||||||
@@ -78,12 +98,16 @@ class SaiRepository {
|
|||||||
suspend fun getById(id: Int): Result<Sai> = withContext(Dispatchers.IO) {
|
suspend fun getById(id: Int): Result<Sai> = 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 {
|
||||||
val sql = "SELECT id_sai, ns FROM SAIs WHERE id_sai = ?"
|
val sql = "SELECT id_sai, ns, modelo FROM SAIs WHERE id_sai = ?"
|
||||||
val statement = connection.prepareStatement(sql)
|
val statement = connection.prepareStatement(sql)
|
||||||
statement.setInt(1, id)
|
statement.setInt(1, id)
|
||||||
val resultSet = statement.executeQuery()
|
val resultSet = statement.executeQuery()
|
||||||
if (resultSet.next()) {
|
if (resultSet.next()) {
|
||||||
Result.success(Sai(id = resultSet.getInt("id_sai"), ns = resultSet.getString("ns")))
|
Result.success(Sai(
|
||||||
|
id = resultSet.getInt("id_sai"),
|
||||||
|
ns = resultSet.getString("ns"),
|
||||||
|
modelo = resultSet.getString("modelo")
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
Result.failure(Exception("not_found"))
|
Result.failure(Exception("not_found"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ package com.example.saipp.data.model
|
|||||||
|
|
||||||
data class Sai(
|
data class Sai(
|
||||||
val id: Int = 0,
|
val id: Int = 0,
|
||||||
val ns: String
|
val ns: String,
|
||||||
|
val modelo: String? = null
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,19 +2,80 @@ package com.example.saipp.ui
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material.icons.filled.QrCodeScanner
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
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.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import com.example.saipp.R
|
import com.example.saipp.R
|
||||||
|
import com.example.saipp.ui.scanner.BarcodeScannerView
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SearchBar(
|
||||||
|
query: String,
|
||||||
|
onQueryChange: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onScanClick: (() -> Unit)? = null
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = query,
|
||||||
|
onValueChange = onQueryChange,
|
||||||
|
modifier = modifier.fillMaxWidth(),
|
||||||
|
placeholder = { Text(stringResource(R.string.placeholder_search)) },
|
||||||
|
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
|
||||||
|
trailingIcon = {
|
||||||
|
if (onScanClick != null) {
|
||||||
|
IconButton(onClick = onScanClick) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.QrCodeScanner,
|
||||||
|
contentDescription = stringResource(R.string.scan_to_search)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
singleLine = true,
|
||||||
|
shape = MaterialTheme.shapes.medium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ScannerDialog(
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onCodeScanned: (String) -> Unit
|
||||||
|
) {
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
properties = DialogProperties(usePlatformDefaultWidth = false)
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
color = MaterialTheme.colorScheme.background
|
||||||
|
) {
|
||||||
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
|
BarcodeScannerView(onBarcodeDetected = { code ->
|
||||||
|
onCodeScanned(code)
|
||||||
|
onDismiss()
|
||||||
|
})
|
||||||
|
|
||||||
|
TextButton(
|
||||||
|
onClick = onDismiss,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.TopStart)
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.button_close), color = Color.White)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ResultDialog(
|
fun ResultDialog(
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package com.example.saipp.ui.management
|
package com.example.saipp.ui.management
|
||||||
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.CalendarMonth
|
||||||
|
import androidx.compose.material.icons.filled.QrCodeScanner
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -17,7 +20,10 @@ import com.example.saipp.data.SaiRepository
|
|||||||
import com.example.saipp.data.model.Assignment
|
import com.example.saipp.data.model.Assignment
|
||||||
import com.example.saipp.data.model.Person
|
import com.example.saipp.data.model.Person
|
||||||
import com.example.saipp.data.model.Sai
|
import com.example.saipp.data.model.Sai
|
||||||
|
import com.example.saipp.ui.ScannerDialog
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -32,8 +38,11 @@ fun AddAssignmentScreen(
|
|||||||
|
|
||||||
var selectedSaiId by remember { mutableStateOf<Int?>(null) }
|
var selectedSaiId by remember { mutableStateOf<Int?>(null) }
|
||||||
var selectedPersonId by remember { mutableStateOf<Int?>(null) }
|
var selectedPersonId by remember { mutableStateOf<Int?>(null) }
|
||||||
var assignedDate by remember { mutableStateOf("") }
|
|
||||||
var returnDate by remember { mutableStateOf("") }
|
var assignedDateDisplay by remember { mutableStateOf("") }
|
||||||
|
var assignedDateDb by remember { mutableStateOf("") }
|
||||||
|
var returnDateDisplay by remember { mutableStateOf("") }
|
||||||
|
var returnDateDb by remember { mutableStateOf("") }
|
||||||
|
|
||||||
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
|
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
|
||||||
var people by remember { mutableStateOf<List<Person>>(emptyList()) }
|
var people by remember { mutableStateOf<List<Person>>(emptyList()) }
|
||||||
@@ -41,11 +50,71 @@ fun AddAssignmentScreen(
|
|||||||
var saiExpanded by remember { mutableStateOf(false) }
|
var saiExpanded by remember { mutableStateOf(false) }
|
||||||
var personExpanded by remember { mutableStateOf(false) }
|
var personExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
var showAssignedDatePicker by remember { mutableStateOf(false) }
|
||||||
|
var showReturnDatePicker by remember { mutableStateOf(false) }
|
||||||
|
var showQuickScanner by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val assignedDatePickerState = rememberDatePickerState()
|
||||||
|
val returnDatePickerState = rememberDatePickerState()
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
saiRepository.getAll().onSuccess { sais = it }
|
saiRepository.getAll().onSuccess { sais = it }
|
||||||
personRepository.getAll().onSuccess { people = it }
|
personRepository.getAll().onSuccess { people = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showAssignedDatePicker) {
|
||||||
|
DatePickerDialog(
|
||||||
|
onDismissRequest = { showAssignedDatePicker = false },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = {
|
||||||
|
assignedDatePickerState.selectedDateMillis?.let { millis ->
|
||||||
|
val date = Date(millis)
|
||||||
|
assignedDateDisplay = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(date)
|
||||||
|
assignedDateDb = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(date)
|
||||||
|
}
|
||||||
|
showAssignedDatePicker = false
|
||||||
|
}) { Text("Aceptar") }
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { showAssignedDatePicker = false }) { Text("Cancelar") }
|
||||||
|
}
|
||||||
|
) { DatePicker(state = assignedDatePickerState) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showReturnDatePicker) {
|
||||||
|
DatePickerDialog(
|
||||||
|
onDismissRequest = { showReturnDatePicker = false },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = {
|
||||||
|
returnDatePickerState.selectedDateMillis?.let { millis ->
|
||||||
|
val date = Date(millis)
|
||||||
|
returnDateDisplay = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(date)
|
||||||
|
returnDateDb = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(date)
|
||||||
|
}
|
||||||
|
showReturnDatePicker = false
|
||||||
|
}) { Text("Aceptar") }
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { showReturnDatePicker = false }) { Text("Cancelar") }
|
||||||
|
}
|
||||||
|
) { DatePicker(state = returnDatePickerState) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showQuickScanner) {
|
||||||
|
ScannerDialog(
|
||||||
|
onDismiss = { showQuickScanner = false },
|
||||||
|
onCodeScanned = { code ->
|
||||||
|
val matchingSai = sais.find { it.ns.equals(code, ignoreCase = true) }
|
||||||
|
if (matchingSai != null) {
|
||||||
|
selectedSaiId = matchingSai.id
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, "SAI no encontrado: $code", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
showQuickScanner = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
@@ -75,6 +144,14 @@ fun AddAssignmentScreen(
|
|||||||
onValueChange = {},
|
onValueChange = {},
|
||||||
readOnly = true,
|
readOnly = true,
|
||||||
label = { Text(stringResource(R.string.label_sai)) },
|
label = { Text(stringResource(R.string.label_sai)) },
|
||||||
|
leadingIcon = {
|
||||||
|
IconButton(onClick = { showQuickScanner = true }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.QrCodeScanner,
|
||||||
|
contentDescription = "Escanear SAI"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = saiExpanded) },
|
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = saiExpanded) },
|
||||||
modifier = Modifier.menuAnchor().fillMaxWidth()
|
modifier = Modifier.menuAnchor().fillMaxWidth()
|
||||||
)
|
)
|
||||||
@@ -129,19 +206,31 @@ fun AddAssignmentScreen(
|
|||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = assignedDate,
|
value = assignedDateDisplay,
|
||||||
onValueChange = { assignedDate = it },
|
onValueChange = {},
|
||||||
|
readOnly = true,
|
||||||
label = { Text(stringResource(R.string.label_assigned_date)) },
|
label = { Text(stringResource(R.string.label_assigned_date)) },
|
||||||
modifier = Modifier.fillMaxWidth()
|
trailingIcon = {
|
||||||
|
IconButton(onClick = { showAssignedDatePicker = true }) {
|
||||||
|
Icon(Icons.Default.CalendarMonth, contentDescription = "Seleccionar fecha")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth().clickable { showAssignedDatePicker = true }
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = returnDate,
|
value = returnDateDisplay,
|
||||||
onValueChange = { returnDate = it },
|
onValueChange = {},
|
||||||
|
readOnly = true,
|
||||||
label = { Text(stringResource(R.string.label_return_date)) },
|
label = { Text(stringResource(R.string.label_return_date)) },
|
||||||
modifier = Modifier.fillMaxWidth()
|
trailingIcon = {
|
||||||
|
IconButton(onClick = { showReturnDatePicker = true }) {
|
||||||
|
Icon(Icons.Default.CalendarMonth, contentDescription = "Seleccionar fecha")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth().clickable { showReturnDatePicker = true }
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(32.dp))
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
@@ -155,8 +244,8 @@ fun AddAssignmentScreen(
|
|||||||
Assignment(
|
Assignment(
|
||||||
idSai = selectedSaiId!!,
|
idSai = selectedSaiId!!,
|
||||||
idPersona = selectedPersonId!!,
|
idPersona = selectedPersonId!!,
|
||||||
assignedDate = assignedDate.takeIf { it.isNotBlank() },
|
assignedDate = assignedDateDb.takeIf { it.isNotBlank() },
|
||||||
returnDate = returnDate.takeIf { it.isNotBlank() }
|
returnDate = returnDateDb.takeIf { it.isNotBlank() }
|
||||||
)
|
)
|
||||||
).onSuccess {
|
).onSuccess {
|
||||||
Toast.makeText(context, "Asignación guardada", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Asignación guardada", Toast.LENGTH_SHORT).show()
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package com.example.saipp.ui.management
|
package com.example.saipp.ui.management
|
||||||
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.CalendarMonth
|
||||||
|
import androidx.compose.material.icons.filled.QrCodeScanner
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -15,7 +18,10 @@ import com.example.saipp.data.BatteryRepository
|
|||||||
import com.example.saipp.data.SaiRepository
|
import com.example.saipp.data.SaiRepository
|
||||||
import com.example.saipp.data.model.Battery
|
import com.example.saipp.data.model.Battery
|
||||||
import com.example.saipp.data.model.Sai
|
import com.example.saipp.data.model.Sai
|
||||||
|
import com.example.saipp.ui.ScannerDialog
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -28,17 +34,64 @@ fun AddBatteryScreen(
|
|||||||
val saiRepository = remember { SaiRepository() }
|
val saiRepository = remember { SaiRepository() }
|
||||||
|
|
||||||
var brand by remember { mutableStateOf("") }
|
var brand by remember { mutableStateOf("") }
|
||||||
var installDate by remember { mutableStateOf("") }
|
var installDateDisplay by remember { mutableStateOf("") } // DD-MM-AAAA
|
||||||
|
var installDateDb by remember { mutableStateOf("") } // YYYY-MM-DD
|
||||||
var selectedSaiId by remember { mutableStateOf<Int?>(null) }
|
var selectedSaiId by remember { mutableStateOf<Int?>(null) }
|
||||||
|
|
||||||
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
|
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
|
||||||
var isSaving by remember { mutableStateOf(false) }
|
var isSaving by remember { mutableStateOf(false) }
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
var showDatePicker by remember { mutableStateOf(false) }
|
||||||
|
var showQuickScanner by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val datePickerState = rememberDatePickerState()
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
saiRepository.getAll().onSuccess { sais = it }
|
saiRepository.getAll().onSuccess { sais = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showDatePicker) {
|
||||||
|
DatePickerDialog(
|
||||||
|
onDismissRequest = { showDatePicker = false },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = {
|
||||||
|
datePickerState.selectedDateMillis?.let { millis ->
|
||||||
|
val date = Date(millis)
|
||||||
|
val displayFormat = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())
|
||||||
|
val dbFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||||
|
installDateDisplay = displayFormat.format(date)
|
||||||
|
installDateDb = dbFormat.format(date)
|
||||||
|
}
|
||||||
|
showDatePicker = false
|
||||||
|
}) {
|
||||||
|
Text("Aceptar")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { showDatePicker = false }) {
|
||||||
|
Text("Cancelar")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
DatePicker(state = datePickerState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showQuickScanner) {
|
||||||
|
ScannerDialog(
|
||||||
|
onDismiss = { showQuickScanner = false },
|
||||||
|
onCodeScanned = { code ->
|
||||||
|
val matchingSai = sais.find { it.ns.equals(code, ignoreCase = true) }
|
||||||
|
if (matchingSai != null) {
|
||||||
|
selectedSaiId = matchingSai.id
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, "SAI no encontrado: $code", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
showQuickScanner = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
@@ -68,6 +121,14 @@ fun AddBatteryScreen(
|
|||||||
onValueChange = {},
|
onValueChange = {},
|
||||||
readOnly = true,
|
readOnly = true,
|
||||||
label = { Text(stringResource(R.string.label_sai)) },
|
label = { Text(stringResource(R.string.label_sai)) },
|
||||||
|
leadingIcon = {
|
||||||
|
IconButton(onClick = { showQuickScanner = true }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.QrCodeScanner,
|
||||||
|
contentDescription = "Escanear SAI"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
||||||
modifier = Modifier.menuAnchor().fillMaxWidth()
|
modifier = Modifier.menuAnchor().fillMaxWidth()
|
||||||
)
|
)
|
||||||
@@ -106,10 +167,18 @@ fun AddBatteryScreen(
|
|||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = installDate,
|
value = installDateDisplay,
|
||||||
onValueChange = { installDate = it },
|
onValueChange = {},
|
||||||
|
readOnly = true,
|
||||||
label = { Text(stringResource(R.string.label_install_date)) },
|
label = { Text(stringResource(R.string.label_install_date)) },
|
||||||
modifier = Modifier.fillMaxWidth()
|
trailingIcon = {
|
||||||
|
IconButton(onClick = { showDatePicker = true }) {
|
||||||
|
Icon(Icons.Default.CalendarMonth, contentDescription = "Seleccionar fecha")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { showDatePicker = true }
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(32.dp))
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
@@ -122,7 +191,7 @@ fun AddBatteryScreen(
|
|||||||
Battery(
|
Battery(
|
||||||
idSai = selectedSaiId,
|
idSai = selectedSaiId,
|
||||||
brand = brand,
|
brand = brand,
|
||||||
installDate = installDate.takeIf { it.isNotBlank() }
|
installDate = installDateDb.takeIf { it.isNotBlank() }
|
||||||
)
|
)
|
||||||
).onSuccess {
|
).onSuccess {
|
||||||
Toast.makeText(context, "Batería añadida", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Batería añadida", Toast.LENGTH_SHORT).show()
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.saipp.data.AssignmentRepository
|
import com.example.saipp.data.AssignmentRepository
|
||||||
import com.example.saipp.data.model.Assignment
|
import com.example.saipp.data.model.Assignment
|
||||||
|
import com.example.saipp.ui.ScannerDialog
|
||||||
|
import com.example.saipp.ui.SearchBar
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -31,6 +35,8 @@ fun AssignmentListScreen(
|
|||||||
val repository = remember { AssignmentRepository() }
|
val repository = remember { AssignmentRepository() }
|
||||||
var items by remember { mutableStateOf<List<Assignment>>(emptyList()) }
|
var items by remember { mutableStateOf<List<Assignment>>(emptyList()) }
|
||||||
var isLoading by remember { mutableStateOf(true) }
|
var isLoading by remember { mutableStateOf(true) }
|
||||||
|
var searchQuery by remember { mutableStateOf("") }
|
||||||
|
var showQuickScanner by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
fun loadItems() {
|
fun loadItems() {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
@@ -46,6 +52,25 @@ fun AssignmentListScreen(
|
|||||||
loadItems()
|
loadItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val filteredItems = remember(items, searchQuery) {
|
||||||
|
items.filter {
|
||||||
|
it.personName?.contains(searchQuery, ignoreCase = true) ?: false ||
|
||||||
|
it.saiNs?.contains(searchQuery, ignoreCase = true) ?: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun formatDate(dbDate: String?): String {
|
||||||
|
if (dbDate == null) return "N/A"
|
||||||
|
return try {
|
||||||
|
val dbFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||||
|
val displayFormat = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())
|
||||||
|
val date = dbFormat.parse(dbDate)
|
||||||
|
if (date != null) displayFormat.format(date) else dbDate
|
||||||
|
} catch (e: Exception) {
|
||||||
|
dbDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
@@ -63,25 +88,39 @@ fun AssignmentListScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) { padding ->
|
) { padding ->
|
||||||
|
Column(modifier = Modifier.fillMaxSize().padding(padding)) {
|
||||||
|
SearchBar(
|
||||||
|
query = searchQuery,
|
||||||
|
onQueryChange = { searchQuery = it },
|
||||||
|
onScanClick = { showQuickScanner = true },
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (showQuickScanner) {
|
||||||
|
ScannerDialog(
|
||||||
|
onDismiss = { showQuickScanner = false },
|
||||||
|
onCodeScanned = { code ->
|
||||||
|
searchQuery = code
|
||||||
|
showQuickScanner = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(modifier = Modifier.weight(1f).fillMaxWidth(), contentAlignment = Alignment.Center) {
|
||||||
CircularProgressIndicator()
|
CircularProgressIndicator()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LazyColumn(
|
LazyColumn(modifier = Modifier.weight(1f).fillMaxWidth()) {
|
||||||
modifier = Modifier
|
items(filteredItems) { item ->
|
||||||
.fillMaxSize()
|
|
||||||
.padding(padding)
|
|
||||||
) {
|
|
||||||
items(items) { item ->
|
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = { Text("${item.personName ?: "Persona desconocida"}") },
|
headlineContent = { Text(item.personName ?: "Persona desconocida") },
|
||||||
supportingContent = {
|
supportingContent = {
|
||||||
Column {
|
Column {
|
||||||
Text("SAI: ${item.saiNs ?: "N/A"}")
|
Text("SAI: ${item.saiNs ?: "N/A"}")
|
||||||
Text("Asignado: ${item.assignedDate ?: "N/A"}")
|
Text("Asignado: ${formatDate(item.assignedDate)}")
|
||||||
if (item.returnDate != null) {
|
if (item.returnDate != null) {
|
||||||
Text("Devolución: ${item.returnDate}")
|
Text("Devolución: ${formatDate(item.returnDate)}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -105,4 +144,5 @@ fun AssignmentListScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.saipp.data.BatteryRepository
|
import com.example.saipp.data.BatteryRepository
|
||||||
import com.example.saipp.data.model.Battery
|
import com.example.saipp.data.model.Battery
|
||||||
|
import com.example.saipp.ui.SearchBar
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -31,6 +34,7 @@ fun BatteryListScreen(
|
|||||||
val repository = remember { BatteryRepository() }
|
val repository = remember { BatteryRepository() }
|
||||||
var items by remember { mutableStateOf<List<Battery>>(emptyList()) }
|
var items by remember { mutableStateOf<List<Battery>>(emptyList()) }
|
||||||
var isLoading by remember { mutableStateOf(true) }
|
var isLoading by remember { mutableStateOf(true) }
|
||||||
|
var searchQuery by remember { mutableStateOf("") }
|
||||||
|
|
||||||
fun loadItems() {
|
fun loadItems() {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
@@ -46,6 +50,25 @@ fun BatteryListScreen(
|
|||||||
loadItems()
|
loadItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val filteredItems = remember(items, searchQuery) {
|
||||||
|
items.filter {
|
||||||
|
it.brand?.contains(searchQuery, ignoreCase = true) ?: false ||
|
||||||
|
it.idSai?.toString()?.contains(searchQuery) ?: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun formatDate(dbDate: String?): String {
|
||||||
|
if (dbDate == null) return "N/A"
|
||||||
|
return try {
|
||||||
|
val dbFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||||
|
val displayFormat = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())
|
||||||
|
val date = dbFormat.parse(dbDate)
|
||||||
|
if (date != null) displayFormat.format(date) else dbDate
|
||||||
|
} catch (e: Exception) {
|
||||||
|
dbDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
@@ -63,22 +86,25 @@ fun BatteryListScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) { padding ->
|
) { padding ->
|
||||||
|
Column(modifier = Modifier.fillMaxSize().padding(padding)) {
|
||||||
|
SearchBar(
|
||||||
|
query = searchQuery,
|
||||||
|
onQueryChange = { searchQuery = it },
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
)
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(modifier = Modifier.weight(1f).fillMaxWidth(), contentAlignment = Alignment.Center) {
|
||||||
CircularProgressIndicator()
|
CircularProgressIndicator()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LazyColumn(
|
LazyColumn(modifier = Modifier.weight(1f).fillMaxWidth()) {
|
||||||
modifier = Modifier
|
items(filteredItems) { item ->
|
||||||
.fillMaxSize()
|
|
||||||
.padding(padding)
|
|
||||||
) {
|
|
||||||
items(items) { item ->
|
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = { Text("Marca: ${item.brand ?: "Desconocida"}") },
|
headlineContent = { Text("Marca: ${item.brand ?: "Desconocida"}") },
|
||||||
supportingContent = {
|
supportingContent = {
|
||||||
Column {
|
Column {
|
||||||
Text("Instalación: ${item.installDate ?: "N/A"}")
|
Text("Instalación: ${formatDate(item.installDate)}")
|
||||||
item.idSai?.let { Text("SAI ID: $it") }
|
item.idSai?.let { Text("SAI ID: $it") }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -102,4 +128,5 @@ fun BatteryListScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package com.example.saipp.ui.management
|
package com.example.saipp.ui.management
|
||||||
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.CalendarMonth
|
||||||
|
import androidx.compose.material.icons.filled.QrCodeScanner
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -17,7 +20,10 @@ import com.example.saipp.data.SaiRepository
|
|||||||
import com.example.saipp.data.model.Assignment
|
import com.example.saipp.data.model.Assignment
|
||||||
import com.example.saipp.data.model.Person
|
import com.example.saipp.data.model.Person
|
||||||
import com.example.saipp.data.model.Sai
|
import com.example.saipp.data.model.Sai
|
||||||
|
import com.example.saipp.ui.ScannerDialog
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -33,8 +39,11 @@ fun EditAssignmentScreen(
|
|||||||
|
|
||||||
var selectedSaiId by remember { mutableStateOf<Int?>(null) }
|
var selectedSaiId by remember { mutableStateOf<Int?>(null) }
|
||||||
var selectedPersonId by remember { mutableStateOf<Int?>(null) }
|
var selectedPersonId by remember { mutableStateOf<Int?>(null) }
|
||||||
var assignedDate by remember { mutableStateOf("") }
|
|
||||||
var returnDate by remember { mutableStateOf("") }
|
var assignedDateDisplay by remember { mutableStateOf("") }
|
||||||
|
var assignedDateDb by remember { mutableStateOf("") }
|
||||||
|
var returnDateDisplay by remember { mutableStateOf("") }
|
||||||
|
var returnDateDb by remember { mutableStateOf("") }
|
||||||
|
|
||||||
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
|
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
|
||||||
var people by remember { mutableStateOf<List<Person>>(emptyList()) }
|
var people by remember { mutableStateOf<List<Person>>(emptyList()) }
|
||||||
@@ -43,6 +52,13 @@ fun EditAssignmentScreen(
|
|||||||
var saiExpanded by remember { mutableStateOf(false) }
|
var saiExpanded by remember { mutableStateOf(false) }
|
||||||
var personExpanded by remember { mutableStateOf(false) }
|
var personExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
var showAssignedDatePicker by remember { mutableStateOf(false) }
|
||||||
|
var showReturnDatePicker by remember { mutableStateOf(false) }
|
||||||
|
var showQuickScanner by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val assignedDatePickerState = rememberDatePickerState()
|
||||||
|
val returnDatePickerState = rememberDatePickerState()
|
||||||
|
|
||||||
LaunchedEffect(assignmentId) {
|
LaunchedEffect(assignmentId) {
|
||||||
val assignmentResult = repository.getById(assignmentId)
|
val assignmentResult = repository.getById(assignmentId)
|
||||||
val saisResult = saiRepository.getAll()
|
val saisResult = saiRepository.getAll()
|
||||||
@@ -52,8 +68,32 @@ fun EditAssignmentScreen(
|
|||||||
val item = assignmentResult.getOrNull()!!
|
val item = assignmentResult.getOrNull()!!
|
||||||
selectedSaiId = item.idSai
|
selectedSaiId = item.idSai
|
||||||
selectedPersonId = item.idPersona
|
selectedPersonId = item.idPersona
|
||||||
assignedDate = item.assignedDate ?: ""
|
|
||||||
returnDate = item.returnDate ?: ""
|
val dbFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||||
|
val displayFormat = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())
|
||||||
|
|
||||||
|
item.assignedDate?.let { dateStr ->
|
||||||
|
try {
|
||||||
|
val date = dbFormat.parse(dateStr)
|
||||||
|
if (date != null) {
|
||||||
|
assignedDateDisplay = displayFormat.format(date)
|
||||||
|
assignedDateDb = dateStr
|
||||||
|
assignedDatePickerState.selectedDateMillis = date.time
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
item.returnDate?.let { dateStr ->
|
||||||
|
try {
|
||||||
|
val date = dbFormat.parse(dateStr)
|
||||||
|
if (date != null) {
|
||||||
|
returnDateDisplay = displayFormat.format(date)
|
||||||
|
returnDateDb = dateStr
|
||||||
|
returnDatePickerState.selectedDateMillis = date.time
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {}
|
||||||
|
}
|
||||||
|
|
||||||
sais = saisResult.getOrDefault(emptyList())
|
sais = saisResult.getOrDefault(emptyList())
|
||||||
people = peopleResult.getOrDefault(emptyList())
|
people = peopleResult.getOrDefault(emptyList())
|
||||||
isLoading = false
|
isLoading = false
|
||||||
@@ -63,6 +103,59 @@ fun EditAssignmentScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showAssignedDatePicker) {
|
||||||
|
DatePickerDialog(
|
||||||
|
onDismissRequest = { showAssignedDatePicker = false },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = {
|
||||||
|
assignedDatePickerState.selectedDateMillis?.let { millis ->
|
||||||
|
val date = Date(millis)
|
||||||
|
assignedDateDisplay = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(date)
|
||||||
|
assignedDateDb = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(date)
|
||||||
|
}
|
||||||
|
showAssignedDatePicker = false
|
||||||
|
}) { Text("Aceptar") }
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { showAssignedDatePicker = false }) { Text("Cancelar") }
|
||||||
|
}
|
||||||
|
) { DatePicker(state = assignedDatePickerState) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showReturnDatePicker) {
|
||||||
|
DatePickerDialog(
|
||||||
|
onDismissRequest = { showReturnDatePicker = false },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = {
|
||||||
|
returnDatePickerState.selectedDateMillis?.let { millis ->
|
||||||
|
val date = Date(millis)
|
||||||
|
returnDateDisplay = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(date)
|
||||||
|
returnDateDb = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(date)
|
||||||
|
}
|
||||||
|
showReturnDatePicker = false
|
||||||
|
}) { Text("Aceptar") }
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { showReturnDatePicker = false }) { Text("Cancelar") }
|
||||||
|
}
|
||||||
|
) { DatePicker(state = returnDatePickerState) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showQuickScanner) {
|
||||||
|
ScannerDialog(
|
||||||
|
onDismiss = { showQuickScanner = false },
|
||||||
|
onCodeScanned = { code ->
|
||||||
|
val matchingSai = sais.find { it.ns.equals(code, ignoreCase = true) }
|
||||||
|
if (matchingSai != null) {
|
||||||
|
selectedSaiId = matchingSai.id
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, "SAI no encontrado: $code", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
showQuickScanner = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
@@ -97,6 +190,14 @@ fun EditAssignmentScreen(
|
|||||||
onValueChange = {},
|
onValueChange = {},
|
||||||
readOnly = true,
|
readOnly = true,
|
||||||
label = { Text(stringResource(R.string.label_sai)) },
|
label = { Text(stringResource(R.string.label_sai)) },
|
||||||
|
leadingIcon = {
|
||||||
|
IconButton(onClick = { showQuickScanner = true }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.QrCodeScanner,
|
||||||
|
contentDescription = "Escanear SAI"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = saiExpanded) },
|
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = saiExpanded) },
|
||||||
modifier = Modifier.menuAnchor().fillMaxWidth()
|
modifier = Modifier.menuAnchor().fillMaxWidth()
|
||||||
)
|
)
|
||||||
@@ -151,19 +252,31 @@ fun EditAssignmentScreen(
|
|||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = assignedDate,
|
value = assignedDateDisplay,
|
||||||
onValueChange = { assignedDate = it },
|
onValueChange = {},
|
||||||
|
readOnly = true,
|
||||||
label = { Text(stringResource(R.string.label_assigned_date)) },
|
label = { Text(stringResource(R.string.label_assigned_date)) },
|
||||||
modifier = Modifier.fillMaxWidth()
|
trailingIcon = {
|
||||||
|
IconButton(onClick = { showAssignedDatePicker = true }) {
|
||||||
|
Icon(Icons.Default.CalendarMonth, contentDescription = "Seleccionar fecha")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth().clickable { showAssignedDatePicker = true }
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = returnDate,
|
value = returnDateDisplay,
|
||||||
onValueChange = { returnDate = it },
|
onValueChange = {},
|
||||||
|
readOnly = true,
|
||||||
label = { Text(stringResource(R.string.label_return_date)) },
|
label = { Text(stringResource(R.string.label_return_date)) },
|
||||||
modifier = Modifier.fillMaxWidth()
|
trailingIcon = {
|
||||||
|
IconButton(onClick = { showReturnDatePicker = true }) {
|
||||||
|
Icon(Icons.Default.CalendarMonth, contentDescription = "Seleccionar fecha")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth().clickable { showReturnDatePicker = true }
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(32.dp))
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
@@ -178,8 +291,8 @@ fun EditAssignmentScreen(
|
|||||||
id = assignmentId,
|
id = assignmentId,
|
||||||
idSai = selectedSaiId!!,
|
idSai = selectedSaiId!!,
|
||||||
idPersona = selectedPersonId!!,
|
idPersona = selectedPersonId!!,
|
||||||
assignedDate = assignedDate.takeIf { it.isNotBlank() },
|
assignedDate = assignedDateDb.takeIf { it.isNotBlank() },
|
||||||
returnDate = returnDate.takeIf { it.isNotBlank() }
|
returnDate = returnDateDb.takeIf { it.isNotBlank() }
|
||||||
)
|
)
|
||||||
).onSuccess {
|
).onSuccess {
|
||||||
Toast.makeText(context, "Asignación actualizada", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Asignación actualizada", Toast.LENGTH_SHORT).show()
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package com.example.saipp.ui.management
|
package com.example.saipp.ui.management
|
||||||
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.CalendarMonth
|
||||||
|
import androidx.compose.material.icons.filled.QrCodeScanner
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -15,7 +18,10 @@ import com.example.saipp.data.BatteryRepository
|
|||||||
import com.example.saipp.data.SaiRepository
|
import com.example.saipp.data.SaiRepository
|
||||||
import com.example.saipp.data.model.Battery
|
import com.example.saipp.data.model.Battery
|
||||||
import com.example.saipp.data.model.Sai
|
import com.example.saipp.data.model.Sai
|
||||||
|
import com.example.saipp.ui.ScannerDialog
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -29,13 +35,18 @@ fun EditBatteryScreen(
|
|||||||
val saiRepository = remember { SaiRepository() }
|
val saiRepository = remember { SaiRepository() }
|
||||||
|
|
||||||
var brand by remember { mutableStateOf("") }
|
var brand by remember { mutableStateOf("") }
|
||||||
var installDate by remember { mutableStateOf("") }
|
var installDateDisplay by remember { mutableStateOf("") }
|
||||||
|
var installDateDb by remember { mutableStateOf("") }
|
||||||
var selectedSaiId by remember { mutableStateOf<Int?>(null) }
|
var selectedSaiId by remember { mutableStateOf<Int?>(null) }
|
||||||
|
|
||||||
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
|
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
|
||||||
var isLoading by remember { mutableStateOf(true) }
|
var isLoading by remember { mutableStateOf(true) }
|
||||||
var isSaving by remember { mutableStateOf(false) }
|
var isSaving by remember { mutableStateOf(false) }
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
var showDatePicker by remember { mutableStateOf(false) }
|
||||||
|
var showQuickScanner by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val datePickerState = rememberDatePickerState()
|
||||||
|
|
||||||
LaunchedEffect(batteryId) {
|
LaunchedEffect(batteryId) {
|
||||||
val batteryResult = batteryRepository.getById(batteryId)
|
val batteryResult = batteryRepository.getById(batteryId)
|
||||||
@@ -44,7 +55,24 @@ fun EditBatteryScreen(
|
|||||||
if (batteryResult.isSuccess) {
|
if (batteryResult.isSuccess) {
|
||||||
val battery = batteryResult.getOrNull()!!
|
val battery = batteryResult.getOrNull()!!
|
||||||
brand = battery.brand ?: ""
|
brand = battery.brand ?: ""
|
||||||
installDate = battery.installDate ?: ""
|
|
||||||
|
// Format existing date for display
|
||||||
|
battery.installDate?.let { dbDate ->
|
||||||
|
try {
|
||||||
|
val dbFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||||
|
val displayFormat = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())
|
||||||
|
val date = dbFormat.parse(dbDate)
|
||||||
|
if (date != null) {
|
||||||
|
installDateDisplay = displayFormat.format(date)
|
||||||
|
installDateDb = dbDate
|
||||||
|
datePickerState.selectedDateMillis = date.time
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
installDateDisplay = dbDate
|
||||||
|
installDateDb = dbDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
selectedSaiId = battery.idSai
|
selectedSaiId = battery.idSai
|
||||||
sais = saisResult.getOrDefault(emptyList())
|
sais = saisResult.getOrDefault(emptyList())
|
||||||
isLoading = false
|
isLoading = false
|
||||||
@@ -54,6 +82,48 @@ fun EditBatteryScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showDatePicker) {
|
||||||
|
DatePickerDialog(
|
||||||
|
onDismissRequest = { showDatePicker = false },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = {
|
||||||
|
datePickerState.selectedDateMillis?.let { millis ->
|
||||||
|
val date = Date(millis)
|
||||||
|
val displayFormat = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())
|
||||||
|
val dbFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||||
|
installDateDisplay = displayFormat.format(date)
|
||||||
|
installDateDb = dbFormat.format(date)
|
||||||
|
}
|
||||||
|
showDatePicker = false
|
||||||
|
}) {
|
||||||
|
Text("Aceptar")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { showDatePicker = false }) {
|
||||||
|
Text("Cancelar")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
DatePicker(state = datePickerState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showQuickScanner) {
|
||||||
|
ScannerDialog(
|
||||||
|
onDismiss = { showQuickScanner = false },
|
||||||
|
onCodeScanned = { code ->
|
||||||
|
val matchingSai = sais.find { it.ns.equals(code, ignoreCase = true) }
|
||||||
|
if (matchingSai != null) {
|
||||||
|
selectedSaiId = matchingSai.id
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, "SAI no encontrado: $code", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
showQuickScanner = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
@@ -88,6 +158,14 @@ fun EditBatteryScreen(
|
|||||||
onValueChange = {},
|
onValueChange = {},
|
||||||
readOnly = true,
|
readOnly = true,
|
||||||
label = { Text(stringResource(R.string.label_sai)) },
|
label = { Text(stringResource(R.string.label_sai)) },
|
||||||
|
leadingIcon = {
|
||||||
|
IconButton(onClick = { showQuickScanner = true }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.QrCodeScanner,
|
||||||
|
contentDescription = "Escanear SAI"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
||||||
modifier = Modifier.menuAnchor().fillMaxWidth()
|
modifier = Modifier.menuAnchor().fillMaxWidth()
|
||||||
)
|
)
|
||||||
@@ -126,10 +204,18 @@ fun EditBatteryScreen(
|
|||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = installDate,
|
value = installDateDisplay,
|
||||||
onValueChange = { installDate = it },
|
onValueChange = {},
|
||||||
|
readOnly = true,
|
||||||
label = { Text(stringResource(R.string.label_install_date)) },
|
label = { Text(stringResource(R.string.label_install_date)) },
|
||||||
modifier = Modifier.fillMaxWidth()
|
trailingIcon = {
|
||||||
|
IconButton(onClick = { showDatePicker = true }) {
|
||||||
|
Icon(Icons.Default.CalendarMonth, contentDescription = "Seleccionar fecha")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { showDatePicker = true }
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(32.dp))
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
@@ -143,7 +229,7 @@ fun EditBatteryScreen(
|
|||||||
id = batteryId,
|
id = batteryId,
|
||||||
idSai = selectedSaiId,
|
idSai = selectedSaiId,
|
||||||
brand = brand,
|
brand = brand,
|
||||||
installDate = installDate.takeIf { it.isNotBlank() }
|
installDate = installDateDb.takeIf { it.isNotBlank() }
|
||||||
)
|
)
|
||||||
).onSuccess {
|
).onSuccess {
|
||||||
Toast.makeText(context, "Batería actualizada", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Batería actualizada", Toast.LENGTH_SHORT).show()
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ import androidx.compose.material3.*
|
|||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
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.R
|
||||||
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 kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -24,12 +26,14 @@ fun EditSaiScreen(
|
|||||||
val repository = remember { SaiRepository() }
|
val repository = remember { SaiRepository() }
|
||||||
|
|
||||||
var ns by remember { mutableStateOf("") }
|
var ns by remember { mutableStateOf("") }
|
||||||
|
var modelo by remember { mutableStateOf("") }
|
||||||
var isLoading by remember { mutableStateOf(true) }
|
var isLoading by remember { mutableStateOf(true) }
|
||||||
var isSaving by remember { mutableStateOf(false) }
|
var isSaving by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
LaunchedEffect(saiId) {
|
LaunchedEffect(saiId) {
|
||||||
repository.getById(saiId).onSuccess {
|
repository.getById(saiId).onSuccess {
|
||||||
ns = it.ns
|
ns = it.ns
|
||||||
|
modelo = it.modelo ?: ""
|
||||||
isLoading = false
|
isLoading = false
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
Toast.makeText(context, "Error al cargar SAI", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Error al cargar SAI", Toast.LENGTH_SHORT).show()
|
||||||
@@ -66,13 +70,20 @@ fun EditSaiScreen(
|
|||||||
label = { Text("Número de Serie") },
|
label = { Text("Número de Serie") },
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
OutlinedTextField(
|
||||||
|
value = modelo,
|
||||||
|
onValueChange = { modelo = it },
|
||||||
|
label = { Text(stringResource(R.string.label_modelo)) },
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
Spacer(modifier = Modifier.height(32.dp))
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
if (ns.isNotBlank()) {
|
if (ns.isNotBlank()) {
|
||||||
isSaving = true
|
isSaving = true
|
||||||
scope.launch {
|
scope.launch {
|
||||||
repository.update(Sai(id = saiId, ns = ns))
|
repository.update(Sai(id = saiId, ns = ns, modelo = modelo))
|
||||||
.onSuccess {
|
.onSuccess {
|
||||||
Toast.makeText(context, "SAI actualizado", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "SAI actualizado", Toast.LENGTH_SHORT).show()
|
||||||
onNavigateBack()
|
onNavigateBack()
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.saipp.data.PersonRepository
|
import com.example.saipp.data.PersonRepository
|
||||||
import com.example.saipp.data.model.Person
|
import com.example.saipp.data.model.Person
|
||||||
|
import com.example.saipp.ui.SearchBar
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -31,6 +32,7 @@ fun PersonListScreen(
|
|||||||
val repository = remember { PersonRepository() }
|
val repository = remember { PersonRepository() }
|
||||||
var items by remember { mutableStateOf<List<Person>>(emptyList()) }
|
var items by remember { mutableStateOf<List<Person>>(emptyList()) }
|
||||||
var isLoading by remember { mutableStateOf(true) }
|
var isLoading by remember { mutableStateOf(true) }
|
||||||
|
var searchQuery by remember { mutableStateOf("") }
|
||||||
|
|
||||||
fun loadItems() {
|
fun loadItems() {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
@@ -46,6 +48,10 @@ fun PersonListScreen(
|
|||||||
loadItems()
|
loadItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val filteredItems = remember(items, searchQuery) {
|
||||||
|
items.filter { it.name.contains(searchQuery, ignoreCase = true) }
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
@@ -63,17 +69,20 @@ fun PersonListScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) { padding ->
|
) { padding ->
|
||||||
|
Column(modifier = Modifier.fillMaxSize().padding(padding)) {
|
||||||
|
SearchBar(
|
||||||
|
query = searchQuery,
|
||||||
|
onQueryChange = { searchQuery = it },
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
)
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(modifier = Modifier.weight(1f).fillMaxWidth(), contentAlignment = Alignment.Center) {
|
||||||
CircularProgressIndicator()
|
CircularProgressIndicator()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LazyColumn(
|
LazyColumn(modifier = Modifier.weight(1f).fillMaxWidth()) {
|
||||||
modifier = Modifier
|
items(filteredItems) { item ->
|
||||||
.fillMaxSize()
|
|
||||||
.padding(padding)
|
|
||||||
) {
|
|
||||||
items(items) { item ->
|
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = { Text(item.name) },
|
headlineContent = { Text(item.name) },
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
@@ -96,4 +105,5 @@ fun PersonListScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
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.ScannerDialog
|
||||||
|
import com.example.saipp.ui.SearchBar
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -31,6 +33,8 @@ fun SaiListScreen(
|
|||||||
val repository = remember { SaiRepository() }
|
val repository = remember { SaiRepository() }
|
||||||
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
|
var sais by remember { mutableStateOf<List<Sai>>(emptyList()) }
|
||||||
var isLoading by remember { mutableStateOf(true) }
|
var isLoading by remember { mutableStateOf(true) }
|
||||||
|
var searchQuery by remember { mutableStateOf("") }
|
||||||
|
var showQuickScanner by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
fun loadSais() {
|
fun loadSais() {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
@@ -46,6 +50,13 @@ fun SaiListScreen(
|
|||||||
loadSais()
|
loadSais()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val filteredSais = remember(sais, searchQuery) {
|
||||||
|
sais.filter {
|
||||||
|
it.ns.contains(searchQuery, ignoreCase = true) ||
|
||||||
|
(it.modelo?.contains(searchQuery, ignoreCase = true) ?: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
@@ -63,19 +74,34 @@ fun SaiListScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) { padding ->
|
) { padding ->
|
||||||
|
Column(modifier = Modifier.fillMaxSize().padding(padding)) {
|
||||||
|
SearchBar(
|
||||||
|
query = searchQuery,
|
||||||
|
onQueryChange = { searchQuery = it },
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
onScanClick = { showQuickScanner = true }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (showQuickScanner) {
|
||||||
|
ScannerDialog(
|
||||||
|
onDismiss = { showQuickScanner = false },
|
||||||
|
onCodeScanned = { code ->
|
||||||
|
searchQuery = code
|
||||||
|
showQuickScanner = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(modifier = Modifier.weight(1f).fillMaxWidth(), contentAlignment = Alignment.Center) {
|
||||||
CircularProgressIndicator()
|
CircularProgressIndicator()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LazyColumn(
|
LazyColumn(modifier = Modifier.weight(1f).fillMaxWidth()) {
|
||||||
modifier = Modifier
|
items(filteredSais) { sai ->
|
||||||
.fillMaxSize()
|
|
||||||
.padding(padding)
|
|
||||||
) {
|
|
||||||
items(sais) { sai ->
|
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = { Text("NS: ${sai.ns}") },
|
headlineContent = { Text("NS: ${sai.ns}") },
|
||||||
|
supportingContent = { Text("Modelo: ${sai.modelo ?: "N/A"}") },
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
Row {
|
Row {
|
||||||
IconButton(onClick = { onNavigateToEdit(sai.id) }) {
|
IconButton(onClick = { onNavigateToEdit(sai.id) }) {
|
||||||
@@ -96,4 +122,5 @@ fun SaiListScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,10 @@ package com.example.saipp.ui.management
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.util.Log
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
@@ -16,9 +13,10 @@ import androidx.compose.runtime.*
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
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 androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.example.saipp.ui.ResultDialog
|
import com.example.saipp.R
|
||||||
import com.example.saipp.data.SaiRepository
|
import com.example.saipp.data.SaiRepository
|
||||||
import com.example.saipp.ui.scanner.BarcodeScannerView
|
import com.example.saipp.ui.scanner.BarcodeScannerView
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -55,6 +53,7 @@ fun SaiScannerScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var scannedBarcode by remember { mutableStateOf<String?>(null) }
|
var scannedBarcode by remember { mutableStateOf<String?>(null) }
|
||||||
|
var modelo by remember { mutableStateOf("") }
|
||||||
var isSaving by remember { mutableStateOf(false) }
|
var isSaving by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@@ -91,21 +90,49 @@ fun SaiScannerScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
scannedBarcode?.let { barcode ->
|
scannedBarcode?.let { barcode ->
|
||||||
ResultDialog(
|
AlertDialog(
|
||||||
barcode = barcode,
|
onDismissRequest = { scannedBarcode = null },
|
||||||
onDismiss = { scannedBarcode = null },
|
title = { Text(text = stringResource(R.string.dialog_title)) },
|
||||||
onConfirm = {
|
text = {
|
||||||
|
Column {
|
||||||
|
Text(text = "Código: $barcode")
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
OutlinedTextField(
|
||||||
|
value = modelo,
|
||||||
|
onValueChange = { modelo = it },
|
||||||
|
label = { Text(stringResource(R.string.label_modelo)) },
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
Button(onClick = {
|
||||||
|
val codeToSave = scannedBarcode!!
|
||||||
scannedBarcode = null
|
scannedBarcode = null
|
||||||
isSaving = true
|
isSaving = true
|
||||||
scope.launch {
|
scope.launch {
|
||||||
repository.insert(barcode)
|
// Check for duplicates
|
||||||
|
val isRegisteredResult = repository.isNsRegistered(codeToSave)
|
||||||
|
if (isRegisteredResult.getOrDefault(false)) {
|
||||||
|
Toast.makeText(context, R.string.error_duplicate_sai, Toast.LENGTH_LONG).show()
|
||||||
|
} else {
|
||||||
|
repository.insert(codeToSave, modelo)
|
||||||
.onSuccess {
|
.onSuccess {
|
||||||
Toast.makeText(context, "SAI guardado", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "SAI guardado", Toast.LENGTH_SHORT).show()
|
||||||
onNavigateBack()
|
onNavigateBack()
|
||||||
}
|
}
|
||||||
.onFailure { Toast.makeText(context, "Error: ${it.message}", Toast.LENGTH_SHORT).show() }
|
.onFailure { Toast.makeText(context, "Error: ${it.message}", Toast.LENGTH_SHORT).show() }
|
||||||
|
}
|
||||||
isSaving = false
|
isSaving = false
|
||||||
}
|
}
|
||||||
|
}) {
|
||||||
|
Text(stringResource(R.string.button_save))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { scannedBarcode = null }) {
|
||||||
|
Text(stringResource(R.string.button_close))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,13 @@
|
|||||||
<string name="no_rows_inserted">No se insertaron filas en 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_sai">SAI</string>
|
||||||
<string name="label_brand">Marca</string>
|
<string name="label_brand">Marca</string>
|
||||||
<string name="label_install_date">Fecha Instalación (AAAA-MM-DD HH:MM:SS)</string>
|
<string name="label_modelo">Modelo</string>
|
||||||
|
<string name="label_install_date">Fecha Instalación (DD-MM-AAAA)</string>
|
||||||
<string name="select_sai">Seleccionar SAI</string>
|
<string name="select_sai">Seleccionar SAI</string>
|
||||||
<string name="no_sai">Ninguno</string>
|
<string name="no_sai">Ninguno</string>
|
||||||
|
<string name="placeholder_search">Buscar...</string>
|
||||||
|
<string name="error_duplicate_sai">Este SAI ya está registrado en el sistema.</string>
|
||||||
|
<string name="scan_to_search">Escanear para buscar</string>
|
||||||
|
|
||||||
<string name="menu_sais">Gestión de SAIs</string>
|
<string name="menu_sais">Gestión de SAIs</string>
|
||||||
<string name="menu_batteries">Gestión de Baterías</string>
|
<string name="menu_batteries">Gestión de Baterías</string>
|
||||||
@@ -25,7 +29,7 @@
|
|||||||
<string name="menu_assignments">Gestión de Asignaciones</string>
|
<string name="menu_assignments">Gestión de Asignaciones</string>
|
||||||
|
|
||||||
<string name="label_person">Persona</string>
|
<string name="label_person">Persona</string>
|
||||||
<string name="label_assigned_date">Fecha Asignación (AAAA-MM-DD)</string>
|
<string name="label_assigned_date">Fecha Asignación (DD-MM-AAAA)</string>
|
||||||
<string name="label_return_date">Fecha Devolución (AAAA-MM-DD)</string>
|
<string name="label_return_date">Fecha Devolución (DD-MM-AAAA)</string>
|
||||||
<string name="select_person">Seleccionar Persona</string>
|
<string name="select_person">Seleccionar Persona</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user