feat: refs#6659 WorkerMistakeScreen

This commit is contained in:
Sergio De la torre 2025-03-24 12:32:19 +01:00
parent 4524e3a887
commit 4ceb51b59e
3 changed files with 643 additions and 0 deletions

View File

@ -0,0 +1,450 @@
package es.verdnatura.presentation.view.feature.workermistake.fragment
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import es.verdnatura.R
import es.verdnatura.presentation.composable.CustomToolbar
import es.verdnatura.presentation.composable.ui.VerdnaturaColors
import es.verdnatura.presentation.view.component.CustomDialogListComposable
import es.verdnatura.presentation.view.feature.workermistake.model.DepartmentMistake
import es.verdnatura.presentation.view.feature.workermistake.model.WorkerFromMistake
@Composable
fun WorkerMistakeScreen(
viewModel: WorkerMistakeViewModel? = null,
title: String,
onBackClick: () -> Unit = {},
) {
val uiStateWorker by viewModel!!.uiStateWorkerDeparment.collectAsStateWithLifecycle()
var filterText by remember { mutableStateOf("") }
var showSearchableList by remember { mutableStateOf(false) }
Box(modifier = Modifier.fillMaxSize()) {
CustomToolbar(
title = title,
subtitle = "",
showBackButton = true,
showSwitch = false,
iconList = listOf(),
onBackClick = { onBackClick() },
onSwitchChange = { },
)
Spacer(modifier = Modifier.height(8.dp))
Column(
modifier = Modifier
.fillMaxSize()
.padding(top = 56.dp)
) {
Box {
HorizontalDivider(
color = Color.White, thickness = 1.dp
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.height(56.dp),
verticalAlignment = Alignment.CenterVertically
) {
TextField(
value = filterText,
onValueChange = { filterText = it },
modifier = Modifier.weight(1f),
placeholder = {
Text(
text = stringResource(R.string.nameSurname),
textAlign = TextAlign.Center,
color = VerdnaturaColors.BlackTextInput,
)
},
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
focusedTextColor = VerdnaturaColors.White,
unfocusedTextColor = VerdnaturaColors.BlackTextInput,
cursorColor = Color.White,
focusedIndicatorColor = Color.DarkGray
),
singleLine = true,
textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center)
)
VerticalDivider(thickness = 4.dp, color = Color.Transparent)
Box(modifier = Modifier.weight(1f)) {
TextField(
value = uiStateWorker.departmentSelected.name.ifEmpty {
stringResource(R.string.buscarDepartamento)
},
onValueChange = { },
modifier = Modifier
.fillMaxWidth()
.clickable { showSearchableList = true },
trailingIcon = {
Icon(painter = painterResource(id = R.drawable.ic_arrow_drop_down_black_24dp),
contentDescription = "Dropdown",
modifier = Modifier.clickable {
showSearchableList = true
viewModel!!.departmentGetHasMistake()
})
},
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
focusedTextColor = VerdnaturaColors.OrangeSalix,
unfocusedTextColor = VerdnaturaColors.OrangeSalix,
focusedIndicatorColor = Color.DarkGray
),
readOnly = true,
singleLine = true,
textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center)
)
}
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(
start = 8.dp, end = 8.dp, top = 8.dp, bottom = 16.dp
), horizontalArrangement = Arrangement.SpaceEvenly
) {
Text(
text = stringResource(id = R.string.name),
modifier = Modifier.weight(1.25f),
textAlign = TextAlign.Start,
color = Color.White,
fontSize = 14.sp
)
Text(
text = stringResource(id = R.string.surname),
modifier = Modifier.weight(1f),
textAlign = TextAlign.Center,
color = Color.White,
fontSize = 14.sp
)
Text(
text = "",
modifier = Modifier.weight(0.5f),
textAlign = TextAlign.Center,
color = Color.White,
fontSize = 14.sp
)
}
HorizontalDivider(thickness = 1.dp, color = Color.LightGray)
LazyColumn {
items(uiStateWorker.workers.filter {
it.firstName.contains(
filterText,
ignoreCase = true
) || it.lastName.contains(filterText, ignoreCase = true)
}) { workerItem ->
WorkerMistakeItem(workerItem) { viewModel!!.setWorkerSelected(workerItem) }
HorizontalDivider(thickness = 1.dp, color = Color.DarkGray)
}
}
}
if (uiStateWorker.workerSelected.id != null) {
CustomDialogListComposable(
title = uiStateWorker.workerSelected.firstName,
buttonOkText = "Cancelar",
description = "Causa del error:",
showRecyclerView = true,
recyclerViewItems = uiStateWorker.mistakeTypes.map { it.description },
onItemSelected = { item ->
println("Item selected: $item")
uiStateWorker.mistakeTypes.find { it.description == item }?.let {
viewModel!!.workerMistakeAdd(uiStateWorker.workerSelected.id!!, it.code)
}
},
showTextInput = false,
onOkClick = { viewModel!!.setWorkerSelected(WorkerFromMistake()) }
)
}
if (showSearchableList) {
SearchableDepartmentsList(modifier = Modifier
.fillMaxWidth()
.align(Alignment.TopStart),
onDepartmentSelected = { department ->
viewModel!!.setDepartment(departmentSelected = department)
showSearchableList = false
},
departmentList = uiStateWorker.departments,
onDismiss = { showSearchableList = false })
}
}
}
@Composable
fun WorkerMistakeItem(workerItem: WorkerFromMistake, clickIcon: () -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = workerItem.firstName,
modifier = Modifier.weight(1.25f),
textAlign = TextAlign.Start,
color = VerdnaturaColors.OrangeSalix
)
Text(
text = workerItem.lastName,
modifier = Modifier.weight(1f),
textAlign = TextAlign.Start,
color = VerdnaturaColors.OrangeSalix
)
IconButton(
onClick = { clickIcon() },
modifier = Modifier.weight(0.5f),
colors = IconButtonDefaults.iconButtonColors(
containerColor = Color.Black, contentColor = VerdnaturaColors.OrangeSalix
)
) {
Icon(
painter = painterResource(id = R.drawable.emoticon_error),
contentDescription = "Action",
)
}
}
}
@Composable
fun SearchableDepartmentsList(
modifier: Modifier = Modifier,
departmentList: List<DepartmentMistake>,
onDepartmentSelected: (DepartmentMistake) -> Unit,
onDismiss: () -> Unit
) {
var searchQuery by remember { mutableStateOf("") }
val filteredDepartments = remember(searchQuery, departmentList) {
if (searchQuery.isEmpty()) {
departmentList
} else {
departmentList.filter {
it.name.contains(searchQuery, ignoreCase = true) || it.name.contains(
searchQuery,
ignoreCase = true
)
}
}
}
Dialog(onDismissRequest = onDismiss) {
Surface(
modifier = modifier
.fillMaxWidth()
.fillMaxHeight(),
shape = RoundedCornerShape(8.dp),
color = MaterialTheme.colorScheme.surface,
shadowElevation = 8.dp
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
IconButton(onClick = onDismiss) {
Icon(
imageVector = Icons.Default.Close, contentDescription = "Cerrar"
)
}
}
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(
value = searchQuery,
onValueChange = { searchQuery = it },
modifier = Modifier.fillMaxWidth(),
placeholder = { Text("Buscar departamento") },
leadingIcon = {
Icon(
imageVector = Icons.Default.Search, contentDescription = "Buscar"
)
},
singleLine = true
)
Spacer(modifier = Modifier.height(16.dp))
// Lista de departamentos
if (filteredDepartments.isEmpty()) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
contentAlignment = Alignment.Center
) {
Text(
text = "No se encontraron departamentos",
textAlign = TextAlign.Center,
color = Color.Gray
)
}
} else {
LazyColumn {
items(filteredDepartments) { department ->
DepartmentItem(department = department, onSelected = {
onDepartmentSelected(department)
})
}
}
}
}
}
}
}
@Composable
private fun DepartmentItem(
department: DepartmentMistake, onSelected: () -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp)
.clickable(onClick = onSelected),
elevation = CardDefaults.cardElevation(
defaultElevation = 2.dp
)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text(
text = department.name,
style = MaterialTheme.typography.bodyLarge,
color = VerdnaturaColors.BlackCustomDialog
)
}
}
}
// Clase de modelo para los elementos de la lista
data class WorkerItem(
val name: String, val surname: String
// Otros campos necesarios
)
// Añade esta función preview debajo de tu WorkerMistakeScreen
@Preview(
showBackground = true,
backgroundColor = 0xFF2E7D32, // Color de fondo verde oscuro similar a verdnatura
widthDp = 360,
heightDp = 640
)
@Composable
fun WorkerMistakeScreenPreview() {
MaterialTheme {
Surface(color = Color(0xFF2E7D32)) { // Fondo verde verdnatura
WorkerMistakeScreen(
viewModel = null, "Error"
)
}
}
}
// Preview para el item individual
@Preview(showBackground = true)
@Composable
fun WorkerMistakeItemPreview() {
MaterialTheme {
Surface(color = Color(0xFF2E7D32)) {
WorkerMistakeItem(
WorkerFromMistake(
firstName = "Juan", lastName = "García"
),
clickIcon = { },
)
}
}
}
// Si quieres probar con diferentes datos, puedes usar un PreviewParameterProvider
class SampleWorkerItemProvider : PreviewParameterProvider<WorkerItem> {
override val values = sequenceOf(
WorkerItem("Ana", "Martínez"),
WorkerItem("Carlos", "López"),
WorkerItem("María", "Rodríguez")
)
}
@Preview(showBackground = true)
@Composable
fun WorkerMistakeItemWithParametersPreview(
@PreviewParameter(SampleWorkerItemProvider::class) workerItem: WorkerFromMistake
) {
MaterialTheme {
Surface(color = Color(0xFF2E7D32)) {
WorkerMistakeItem(workerItem, clickIcon = { })
}
}
}

View File

@ -0,0 +1,151 @@
package es.verdnatura.presentation.view.feature.workermistake.fragment
import androidx.lifecycle.viewModelScope
import es.verdnatura.MobileApplication
import es.verdnatura.domain.SalixCallback
import es.verdnatura.presentation.base.BaseViewModelCompose
import es.verdnatura.presentation.view.feature.workermistake.model.DepartmentMistake
import es.verdnatura.presentation.view.feature.workermistake.model.MistakeType
import es.verdnatura.presentation.view.feature.workermistake.model.WorkerFromMistake
import es.verdnatura.presentation.view.feature.workermistake.model.WorkerMistakeSalix
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import retrofit2.Response
data class UiStateWorkerDeparment(
val departments: List<DepartmentMistake> = emptyList(),
val workers: List<WorkerFromMistake> = emptyList(),
val departmentSelected: DepartmentMistake = DepartmentMistake(-1, ""),
val mistakeTypes: List<MistakeType> = emptyList(),
val workerSelected: WorkerFromMistake = WorkerFromMistake(),
val isLoading: Boolean = false
)
class WorkerMistakeViewModel(var application: android.app.Application) :
BaseViewModelCompose(application) {
private val _uiStateWorkerDeparment = MutableStateFlow(UiStateWorkerDeparment())
val uiStateWorkerDeparment = _uiStateWorkerDeparment.asStateFlow()
init {
viewModelScope.launch {
val departmendId =
(application as MobileApplication).dataStoreApp.readDataStoreKey<Int>("DEPARTMENTMISTAKEID")
val departmentName =
(application as MobileApplication).dataStoreApp.readDataStoreKey<String>("DEPARTMENTMISTAKE")
if (departmendId != -1) {
_uiStateWorkerDeparment.update {
it.copy(
departmentSelected = DepartmentMistake(departmendId, departmentName)
)
}
workerGetFromHasMistake(departmendId)
} else {
viewModelScope.launch { departmentGetHasMistake() }
}
}
}
fun setDepartment(departmentSelected: DepartmentMistake) {
viewModelScope.launch {
(application as MobileApplication).dataStoreApp.editDataStoreKey(
"DEPARTMENTMISTAKE",
departmentSelected.name
)
(application as MobileApplication).dataStoreApp.editDataStoreKey(
"DEPARTMENTMISTAKEID",
departmentSelected.id
)
_uiStateWorkerDeparment.update {
it.copy(
departmentSelected = departmentSelected
)
}
workerGetFromHasMistake(departmentSelected.id)
}
}
fun setWorkerSelected(woker: WorkerFromMistake) {
viewModelScope.launch {
_uiStateWorkerDeparment.update {
it.copy(
workerSelected = woker
)
}
workerMistakeTypeGet()
}
}
fun departmentGetHasMistake() {
salix.departmentGetHasMistake().enqueue(object :
SalixCallback<List<DepartmentMistake>>(application.applicationContext) {
override fun onSuccess(response: Response<List<DepartmentMistake>>) {
response.body()?.let { list ->
_uiStateWorkerDeparment.update {
it.copy(
departments = list, isLoading = false
)
}
}
}
})
}
private fun workerGetFromHasMistake(departmentFk: Number) {
salix.workerGetFromHasMistake(arrayListOf(departmentFk)).enqueue(object :
SalixCallback<List<WorkerFromMistake>>(application.applicationContext) {
override fun onSuccess(response: Response<List<WorkerFromMistake>>) {
response.body()?.let { list ->
_uiStateWorkerDeparment.update {
it.copy(
workers = list, isLoading = false
)
}
}
}
})
}
private fun workerMistakeTypeGet() {
salix.workerMistakesTypes()
.enqueue(object : SalixCallback<List<MistakeType>>(application.applicationContext) {
override fun onSuccess(response: Response<List<MistakeType>>) {
response.body()?.let { list ->
_uiStateWorkerDeparment.update {
it.copy(
mistakeTypes = list, isLoading = false
)
}
}
}
})
}
fun workerMistakeAdd(
workerFk: Int, typeFk: String
) {
salix.workerMistakesAdd(WorkerMistakeSalix(workerFk, typeFk))
.enqueue(object : SalixCallback<Any>(application.applicationContext) {
override fun onSuccess(response: Response<Any>) {
super.onSuccess(response)
setWorkerSelected(WorkerFromMistake())
}
override fun onError(t: Throwable) {
super.onError(t)
setWorkerSelected(WorkerFromMistake())
}
})
}
}

View File

@ -0,0 +1,42 @@
package es.verdnatura.presentation.view.feature.workermistake.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import es.verdnatura.presentation.view.feature.main.activity.MainActivity
import org.koin.androidx.viewmodel.ext.android.viewModel
class WorkermistakeFragmentCompose(
var entryPoint: String = ""
) : Fragment() {
private val viewModel: WorkerMistakeViewModel by viewModel()
companion object {
fun newInstance(entryPoint: String) = WorkermistakeFragmentCompose(entryPoint)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
WorkerMistakeScreen(
viewModel = viewModel,
title = entryPoint,
onBackClick = { (context as MainActivity).onMyBackPressed() }
)
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(context as MainActivity).hideBottomNavigation(View.GONE)
}
}