feat: refs#6659 jetPackCompose

This commit is contained in:
Sergio De la torre 2025-03-14 13:38:19 +01:00
parent 16b84ce2ec
commit aede2d4051
14 changed files with 1013 additions and 58 deletions

View File

@ -2,6 +2,7 @@ package es.verdnatura.presentation.composable
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -14,13 +15,18 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.PlainTooltip
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TooltipBox
import androidx.compose.material3.TooltipDefaults
import androidx.compose.material3.rememberTooltipState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -29,7 +35,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.colorResource
@ -45,15 +50,16 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import es.verdnatura.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CustomToolbar(
title: String,
subtitle: String? = null,
showBackButton: Boolean = true,
showSwitch: Boolean = false,
iconList: List<Int> = emptyList(),
iconList: List<IconToolBar> = emptyList(),
onBackClick: () -> Unit = {},
onSwitchChange: (Boolean) -> Unit = {}
onSwitchChange: (Boolean) -> Unit = {},
) {
Column(
modifier = Modifier
@ -66,8 +72,9 @@ fun CustomToolbar(
.fillMaxWidth()
.height(56.dp),
verticalAlignment = CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
) {
if (showBackButton) {
IconButton(
onClick = { onBackClick() },
@ -81,6 +88,7 @@ fun CustomToolbar(
}
}
Text(
modifier = Modifier.weight(1f),
text = title,
color = Color.White,
fontSize = dimensionResource(id = R.dimen.title).value.sp,
@ -96,22 +104,40 @@ fun CustomToolbar(
fontSize = dimensionResource(id = R.dimen.subtitle).value.sp
)
}
}
if (iconList.isNotEmpty()) {
LazyRow {
items(iconList) { iconRes ->
IconButton(onClick = { /* Acción del icono */ }) {
Icon(
painter = painterResource(id = iconRes),
contentDescription = "Icon",
tint = Color.White
)
if (iconList.isNotEmpty()) {
LazyRow(
horizontalArrangement = Arrangement.End, // Esto asegura que los iconos estén a la derecha
) {
items(iconList) { iconToolBar ->
TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
tooltip = {
PlainTooltip {
Text(iconToolBar.toolTip)
}
},
state = rememberTooltipState()
) {
IconButton(
onClick = { iconToolBar.onClickIcon() },
) {
Icon(
painter = painterResource(id = iconToolBar.idRes),
contentDescription = iconToolBar.toolTip,
tint = Color.White
)
}
}
}
}
}
}
if (showSwitch) {
var switchState by remember { mutableStateOf(false) }
Switch(
@ -131,6 +157,14 @@ fun CustomToolbar(
)
}
data class IconToolBar(
val idRes: Int,
val toolTip: String = "",
val tint: Color = Color.White,
val onClickIcon: () -> Unit,
)
@Composable
fun ScanLineTextSearch(
value: String,
@ -153,17 +187,23 @@ fun ScanLineTextSearch(
textAlign = TextAlign.Center
),
placeholder = {
Text(
text = stringResource(id = R.string.Escaneaetiqueta),
textAlign = TextAlign.Center,
color = Color.White,
modifier = Modifier.fillMaxWidth()
)
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Text(
text = stringResource(id = R.string.Escaneaetiqueta),
textAlign = TextAlign.Center,
color = Color.White,
modifier = Modifier.fillMaxWidth()
)
}
},
singleLine = true,
keyboardActions = KeyboardActions(
onDone = {
onImeAction()
keyboardController?.hide()
}
),
@ -182,13 +222,12 @@ fun ScanLineTextSearch(
modifier = modifier
.fillMaxWidth(fraction = 0.67f)
.border(2.dp, Color.White, RoundedCornerShape(8.dp))
.height(54.dp)
.padding(bottom = 4.dp)
.onFocusEvent {
.height(52.dp)
/* .onFocusEvent {
if (it.isFocused) {
keyboardController?.hide()
}
}
}*/
)
}
@ -202,7 +241,12 @@ fun PreviewToolbar() {
subtitle = "10/20",
showBackButton = true,
showSwitch = true,
iconList = listOf(R.drawable.ic_launcher_foreground, R.drawable.ic_launcher_foreground),
iconList = listOf(
IconToolBar(
R.drawable.ic_parking_ui, "", tint = Color.Blue,
onClickIcon = { }
)
),
onBackClick = { /* Acción de volver atrás */ },
onSwitchChange = { /* Acción del switch */ }
)
@ -212,7 +256,7 @@ fun PreviewToolbar() {
@Composable
fun PreviewScanLineTextSearch() {
ScanLineTextSearch(
value = "Escanea etiqueta",
value = stringResource(R.string.scanLabel),
onValueChange = {},
onImeAction = {})
}

View File

@ -1,4 +1,4 @@
package es.verdnatura.presentation.view.feature.buscaritem.fragment
package es.verdnatura.presentation.composable
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn

View File

@ -0,0 +1,71 @@
package es.verdnatura.presentation.composable.ui
import androidx.compose.ui.graphics.Color
object VerdnaturaColors {
val OrangeSalix = Color(0xFFEC8916)
val BlackCustomDialog = Color(0xFF1A1A1A)
val White = Color(0xFFFFFFFF)
// Colores principales
val ColorPrimary = Color(0xFFF7931E)
val ColorPrimaryDark = Color(0xFF8B4200)
// Negros
/* val Black = Color(0xFF000000)
val Black1 = Color(0xFF1D1D1D)
val Black2 = Color(0xFF171717)
val Black3 = Color(0xFF292929)
val Black4 = Color(0xFF242424)
val Black5 = Color(0xFF323232)
val Black6 = Color(0xFF333333)
val Black7 = Color(0xFF282828)
val Black8 = Color(0xFF1A1A1A)
val BlackCustomDialog = Color(0xFF464446)
val Black8Alpha6 = Color(0x991A1A1A) // Con transparencia
// Grises
val WarmGrey = Color(0xFF707070)
val BrownGrey = Color(0xFF8F8F8F)
val BrownGreyLight = Color(0xFFB8ADAD)
// Colores vivos
val Red = Color(0xFFE74C3C)
val WarmBrown = Color(0xFF8B4200)
val PumpkinOrange = Color(0xFFF7931E)
val PumpkinLight = Color(0xFFF77956)
val PinkSalix = Color(0xFFFF99CC)
val SunflowerYellow = Color(0xFFFFD400)
val DarkSkyBlue = Color(0xFF4AB4E6)
val DarkGreenVerdnatura = Color(0xFFA3D131)
val DarkMint = Color(0xFF50BE87)
val DarkMintLight = Color(0xFF80BE87)
val DarkMintLightPrecontrolled = Color(0xFFB8DABA)
val LightTeal = Color(0xFFB8ECD6)
val White = Color(0xFFFFFFFF)
val RedSalix = Color(0xFFFB5252)
val OrangeSalix = Color(0xFFEC8916)
// Fondos
val BackgroundItemPicker = Color(0xFF4D4D4D)
val BackgroundSubtitleSettings = Color(0xFF1A1A1A)
val BackgroundItemsMenus = Color(0xFF333333)
// Colores SALIX
val SalixSuccessLight = Color(0xFFA3D131)
val ColorHeader = Color(0xFF3D3D3D)
val ColorMenuHeader = Color(0xFF3D3D3D)
val ColorBg = Color(0xFF222222)
val ColorBgDark = Color(0xFF222222)
val ColorActive = Color(0xFF666666)
val ColorActiveFont = Color(0xFFFFFFFF)
val ColorBgPanel = Color(0xFF3C3B3B)
val ColorMain = ColorPrimary
val ColorMarginal = Color(0xFF222222)
val ColorSuccess = Color(0xFFA3D131)
val ColorNotice = Color(0xFF32B1CE)
val ColorAlert = Color(0xFFFA3939)
val ColorPink = Color(0xFFFF99CC)
val ColorYellow = Color(0xFFFFFF00)*/
}

View File

@ -0,0 +1,167 @@
package es.verdnatura.presentation.composable.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun DefaultButtonCustomDialog(
text: String,
onClick: () -> Unit = {},
enabled: Boolean = true,
leadingIcon: @Composable (() -> Unit)? = null
) {
Button(
modifier = Modifier.fillMaxWidth(),
onClick = onClick,
enabled = enabled,
contentPadding = PaddingValues(
start = if (leadingIcon == null) 16.dp else 8.dp,
end = 16.dp
),
shape = MaterialTheme.shapes.small,
colors = ButtonDefaults.buttonColors(
containerColor = VerdnaturaColors.ColorPrimary,
contentColor = VerdnaturaColors.White
)
) {
if (leadingIcon != null) {
leadingIcon()
}
Text(
text = text.uppercase(),
fontSize = 14.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
@Composable
fun TitleTextCustomDialog(
title: String,
) {
Text(
text = title,
style = MaterialTheme.typography.titleLarge.copy(
fontWeight = FontWeight.Bold
),
color = VerdnaturaColors.White,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 8.dp)
)
}
@Composable
fun DescriptionTextCustomDialog(
description: String,
) {
Text(
text = description,
color = VerdnaturaColors.White,
modifier = Modifier.padding(bottom = 8.dp)
)
}
@Composable
fun ScanTextCustomDialog(
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
hint: String,
onEnterPressed: (String) -> Unit = {},
focusRequester: FocusRequester
) {
val keyboardController = LocalSoftwareKeyboardController.current
TextField(
value = value,
onValueChange = onValueChange,
label = {
Text(
text = hint,
textAlign = TextAlign.Center,
color = Color.White,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
},
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
.border(1.dp, Color.White, shape = MaterialTheme.shapes.small)
.background(Color.Transparent)
.height(VerdnaturaDimens.minHeight),
textStyle = CustomTextStyle,
keyboardOptions =
KeyboardOptions.Default.copy(
imeAction = ImeAction.Done,
keyboardType = KeyboardType.Number
),
singleLine = true,
keyboardActions = KeyboardActions(
onDone = {
onEnterPressed(value.text)
onValueChange(TextFieldValue(""))
keyboardController?.hide()
}
),
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
focusedTextColor = VerdnaturaColors.White,
unfocusedTextColor = VerdnaturaColors.White
)
)
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
}
@Preview(showBackground = true)
@Composable
fun PreviewCustomTextField() {
val sampleText = remember { mutableStateOf(TextFieldValue("Texto de ejemplo")) }
val focusRequester = remember { FocusRequester() }
ScanTextCustomDialog(
value = sampleText.value,
onValueChange = { sampleText.value = it },
hint = "Escribe algo...",
onEnterPressed = { text -> println("Texto ingresado: $text") },
focusRequester = focusRequester
)
}

View File

@ -0,0 +1,23 @@
package es.verdnatura.presentation.composable.ui
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
object VerdnaturaDimens {
/* val h1 = 96.sp
val h2 = 60.sp
val h3 = 48.sp
val h4 = 34.sp
val h5 = 24.sp
val h6 = 20.sp
val h7 = 18.sp
val h9 = 14.sp*/
// Text sizes
val fontSizeCustomDialogs = 16.sp
// Height Scan Text
val minHeight = 48.dp
}

View File

@ -0,0 +1,10 @@
package es.verdnatura.presentation.composable.ui
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
val CustomTextStyle = TextStyle(
color = VerdnaturaColors.White,
fontSize = VerdnaturaDimens.fontSizeCustomDialogs,
textAlign = TextAlign.Center
)

View File

@ -0,0 +1,119 @@
package es.verdnatura.presentation.view.component
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
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.material3.Text
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.focus.FocusRequester
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import es.verdnatura.presentation.composable.ui.DefaultButtonCustomDialog
import es.verdnatura.presentation.composable.ui.DescriptionTextCustomDialog
import es.verdnatura.presentation.composable.ui.ScanTextCustomDialog
import es.verdnatura.presentation.composable.ui.TitleTextCustomDialog
import es.verdnatura.presentation.composable.ui.VerdnaturaColors
@Composable
fun CustomDialogListComposable(
title: String = "",
description: String = "",
buttonOkText: String = "",
onOkClick: () -> Unit = {},
showRecyclerView: Boolean = true,
recyclerViewItems: List<Number> = listOf(),
onItemSelected: (Number) -> Unit = {},
hintOne: String = "",
onEnterPressed: (String) -> Unit = {}
) {
var valueOne by remember { mutableStateOf(TextFieldValue()) }
val showDialog = remember { mutableStateOf(true) }
val keyboardController = LocalSoftwareKeyboardController.current
val focusRequester = remember { FocusRequester() }
if (showDialog.value) {
Dialog(
onDismissRequest = { showDialog.value = false },
) {
Column(
modifier = Modifier
.background(VerdnaturaColors.BlackCustomDialog)
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
if (title.isNotEmpty()) {
TitleTextCustomDialog(title)
}
if (description.isNotEmpty()) {
DescriptionTextCustomDialog(description)
}
ScanTextCustomDialog(
value = valueOne,
onValueChange = { valueOne = it },
hint = hintOne,
onEnterPressed = onEnterPressed,
focusRequester = focusRequester
)
Spacer(modifier = Modifier.height(8.dp))
if (showRecyclerView && recyclerViewItems.isNotEmpty()) {
LazyColumn(modifier = Modifier.fillMaxWidth()) {
items(recyclerViewItems) { item ->
Text(
text = item.toString(),
color = VerdnaturaColors.White,
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.clickable {
onItemSelected(item)
}
)
}
}
}
DefaultButtonCustomDialog(buttonOkText, onClick = {
onOkClick()
keyboardController?.hide()
})
}
}
}
}
@Preview
@Composable
fun PreviewCustomDialogList() {
CustomDialogListComposable(
title = "Custom Dialog Title",
description = "This is a description.",
buttonOkText = "OK",
onOkClick = { /* Handle OK click */ },
showRecyclerView = true,
recyclerViewItems = listOf(1, 2, 3),
onItemSelected = { item -> println("Item selected: $item") },
hintOne = "Hint for input one",
)
}

View File

@ -11,7 +11,8 @@ import retrofit2.Response
data class UiState(
val itemFk: String? = null,
val items: List<ItemShelvings> = emptyList()
val items: List<ItemShelvings> = emptyList(),
val isLoading: Boolean = false
)
class BuscarItemComposeViewModel(var application: android.app.Application) :
@ -21,23 +22,8 @@ class BuscarItemComposeViewModel(var application: android.app.Application) :
private val _uiState = MutableStateFlow(UiState())
val uiState = _uiState.asStateFlow()
/* private val _itemFk = MutableStateFlow<String?>(null)
val itemFk = _itemFk.asStateFlow()*/
/* private val _itemShelvingsList = MutableStateFlow<ItemShelvingsList?>(null)
val itemShelvingsList = _itemShelvingsList.asStateFlow()*/
/*val loadItemShelvingsList = _itemShelvingsList.map {
it?.let { Event(it) }
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = null
)*/
fun getIdFromCodeSalix(code: String) {
_uiState.update { it.copy(itemFk = code) }
//_itemFk.value = code
_uiState.update { it.copy(itemFk = code, isLoading = true) }
getItemFromBarcodeUseCase.execute(code)
.enqueue(object : SalixCallback<Int?>(application.applicationContext) {
override fun onSuccess(response: Response<Int?>) {
@ -45,7 +31,13 @@ class BuscarItemComposeViewModel(var application: android.app.Application) :
itemshelvingsGet(id)
}
}
})
override fun onError(t: Throwable) {
super.onError(t)
_uiState.update { it.copy(isLoading = false) }
}
}
)
}
fun itemshelvingsGet(itemFk: Any) {
@ -57,10 +49,14 @@ class BuscarItemComposeViewModel(var application: android.app.Application) :
.enqueue(object : SalixCallback<List<ItemShelvings>>(application.applicationContext) {
override fun onSuccess(response: Response<List<ItemShelvings>>) {
response.body()?.let { list ->
//_itemShelvingsList.value = ItemShelvingsList(list)
_uiState.update { it.copy(items = list) }
_uiState.update { it.copy(items = list, isLoading = false) }
}
}
override fun onError(t: Throwable) {
super.onError(t)
_uiState.update { it.copy(isLoading = false) }
}
})
}
}

View File

@ -6,10 +6,8 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import es.verdnatura.R
import es.verdnatura.domain.toast
import es.verdnatura.presentation.common.OnPasillerosItemClickListener
@ -39,7 +37,18 @@ class BuscarItemFragmentCompose(
): View {
return ComposeView(requireContext()).apply {
setContent {
SetView()
BuscarItemScreen(
viewModel = viewModel,
onBackClick = { (context as MainActivity).onMyBackPressed() },
onItemLongClick = { sector -> sector.toast(requireContext()) },
onItemClick = { shelving ->
pasillerosItemClickListener?.onPasillerosItemClickListener(
PasillerosItemVO(title = R.string.titleUbicator),
shelving
)
},
title = getString(R.string.getubication)
)
}
}
}
@ -68,12 +77,16 @@ class BuscarItemFragmentCompose(
@Composable
private fun SetView() {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
}
/*Como estaba
LocationScreen(
items = uiState.items.map {
LocationItem(
parking = it.shelving.parking.code,
parking = it.shelving.parking!!.code,
matricula = it.shelving.code,
visible = it.visible.toString(),
reserve = it.available.toString(),
@ -85,7 +98,7 @@ class BuscarItemFragmentCompose(
onTextChange = { input ->
viewModel.getIdFromCodeSalix(input)
},
titleToolBar = if (uiState.items.isNotEmpty()) {
titleToolBar = if (uiState.itemFk != null) {
getString(
R.string.itemsTotal,
uiState.itemFk,
@ -106,7 +119,6 @@ class BuscarItemFragmentCompose(
}
/*
class BuscarItemViewModelFactory(private val context: Application) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(BuscarItemComposeViewModel::class.java)) {

View File

@ -22,24 +22,196 @@ 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.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import es.verdnatura.R
import es.verdnatura.presentation.composable.CustomToolbar
import es.verdnatura.presentation.composable.LottieLoadingAnimation
import es.verdnatura.presentation.composable.ScanLineTextSearch
@Composable
fun BuscarItemScreen(
viewModel: BuscarItemComposeViewModel,
onBackClick: () -> Unit,
onItemLongClick: (sector: String) -> Unit,
onItemClick: (matricula: String) -> Unit,
title: String
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val locationItems = uiState.items
.filter { it.shelving.parking != null }
.map {
LocationItem(
parking = it.shelving.parking!!.code,
matricula = it.shelving.code,
visible = it.visible.toString(),
reserve = it.available.toString(),
priority = it.shelving.priority.toString(),
fecha = it.created,
sector = it.shelving.parking.sector.description
)
}
LocationScreenContent(
items = locationItems,
isLoading = uiState.isLoading,
titleToolBar = if (uiState.itemFk != null) {
LocalContext.current.getString(
R.string.itemsTotal,
uiState.itemFk,
LocalContext.current.getString(R.string.visibleTotal),
uiState.items
.filter { it.shelving.parking != null }
.sumOf { it.visible }
)
} else title,
onTextChange = { input -> viewModel.getIdFromCodeSalix(input) },
onBackClick = onBackClick,
onLongClick = onItemLongClick,
onClick = onItemClick,
modifier = Modifier
)
}
@Composable
private fun LocationScreenContent(
items: List<LocationItem>,
isLoading: Boolean,
titleToolBar: String,
onTextChange: (String) -> Unit,
onBackClick: () -> Unit,
onLongClick: (String) -> Unit,
onClick: (String) -> Unit,
modifier: Modifier
) {
var searchText by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current
val onImeAction: () -> Unit = {
onTextChange(searchText)
searchText = ""
keyboardController?.hide()
}
Box(
modifier = modifier
.fillMaxSize()
.background(Color.Black)
) {
Column(
modifier = modifier
.fillMaxSize()
.background(Color.Black)
) {
CustomToolbar(
title = titleToolBar,
subtitle = "",
showBackButton = true,
showSwitch = false,
iconList = listOf(),
onBackClick = onBackClick,
onSwitchChange = { },
)
Spacer(modifier = Modifier.height(8.dp))
ScanLineTextSearch(
value = searchText,
onValueChange = {
searchText = it
},
onImeAction,
modifier = Modifier
.focusRequester(focusRequester)
.onFocusEvent {
if (it.isFocused) {
keyboardController?.hide() // Asegura que el teclado no se abra
}
}
)
LaunchedEffect(Unit) {
focusRequester.requestFocus()
keyboardController?.hide()
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
horizontalArrangement = Arrangement.Start
) {
listOf(
R.string.parking,
R.string.Matrícula,
R.string.Visible,
R.string.reserve,
R.string.priority,
R.string.date
).forEach { textRes ->
Text(
text = stringResource(id = textRes),
color = Color.White,
fontSize = 14.sp,
modifier = Modifier.weight(1f)
)
}
}
HorizontalDivider(thickness = 1.dp, color = Color.White)
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
items(items) { item ->
LocationRow(
item = item,
onLongClick = { selectedItem ->
onLongClick(selectedItem.sector)
},
onClick = { selectedItem ->
onClick(selectedItem.matricula)
},
)
HorizontalDivider(thickness = 1.dp, color = Color.White)
}
}
}
if (isLoading) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.7f)),
contentAlignment = Alignment.Center
) {
LottieLoadingAnimation(true)
}
}
}
}
// Como estaba
@Composable
fun LocationScreen(
//viewModel: BuscarItemComposeViewModel = viewModel(), // Falta ver de arreglar con koin o viewmodel → No necesario, en el Fragment observa
//viewModel: ViewModel?, // Falta ver de arreglar con koin o viewmodel → No necesario, en el Fragment observa
items: List<LocationItem>,
onTextChange: (String) -> Unit,
titleToolBar: String,
@ -48,6 +220,7 @@ fun LocationScreen(
onLongClick: (LocationItem) -> Unit,
onClick: (LocationItem) -> Unit
) {
var searchText by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current
@ -117,7 +290,7 @@ fun LocationScreen(
R.string.Visible,
R.string.reserve,
R.string.priority,
R.string.Fecha
R.string.date
).forEach { textRes ->
Text(
text = stringResource(id = textRes),
@ -193,7 +366,6 @@ data class LocationItem(
@Composable
fun PreviewLocationScreen() {
LocationScreen(
// viewModel = null,
items = listOf(
LocationItem("P1", "1234ABC", "", "No", "Alta", "12/02/2025", "previa"),
LocationItem("P2", "5678DEF", "No", "", "Baja", "13/02/2025", "previa")

View File

@ -0,0 +1,215 @@
package es.verdnatura.presentation.view.feature.diadeventa.fragment
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
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.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.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
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.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat
import es.verdnatura.R
import es.verdnatura.presentation.composable.CustomToolbar
import es.verdnatura.presentation.composable.IconToolBar
import es.verdnatura.presentation.composable.LottieLoadingAnimation
import es.verdnatura.presentation.composable.ScanLineTextSearch
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@Composable
fun GenericListScreen(
items: List<List<String>>, // Lista de listas de strings para los datos
headers: List<String>,// Lista de encabezados
columnActions: List<(List<String>) -> Unit>, // Lista de acciones
onTextChange: (String) -> Unit,
titleToolBar: String,
onBackClick: () -> Unit = {},
onLongClick: (List<String>) -> Unit,
onClick: (List<String>) -> Unit,
listIconToolBar: List<IconToolBar> = listOf()
) {
var searchText by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current
var showLoading by remember { mutableStateOf(false) }
val onImeAction: () -> Unit = {
onTextChange(searchText)
searchText = ""
keyboardController?.hide()
}
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
) {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
) {
if (showLoading) {
LottieLoadingAnimation(true)
}
CustomToolbar(
title = titleToolBar,
subtitle = "",
showBackButton = true,
showSwitch = false,
iconList = listIconToolBar,
onBackClick = onBackClick,
onSwitchChange = { }
)
Spacer(modifier = Modifier.height(8.dp))
ScanLineTextSearch(
value = searchText,
onValueChange = {
searchText = it
showLoading = true
},
onImeAction,
modifier = Modifier
.focusRequester(focusRequester)
.onFocusEvent {
if (it.isFocused) {
keyboardController?.hide()
}
}
)
LaunchedEffect(Unit) {
focusRequester.requestFocus()
keyboardController?.hide()
}
// Encabezados
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
horizontalArrangement = Arrangement.SpaceEvenly
) {
headers.forEach { text ->
Text(
text = text,
color = Color.White,
fontSize = 14.sp,
modifier = Modifier.weight(1f)
)
}
}
HorizontalDivider(thickness = 1.dp, color = Color.White)
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(items) { item ->
GenericRow(
values = item,
onLongClick = { onLongClick(item) },
onClick = { onClick(item) },
columnActions = columnActions
)
HorizontalDivider(thickness = 1.dp, color = Color.White)
}
}
showLoading = false
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun GenericRow(
values: List<String>,
onLongClick: () -> Unit,
onClick: () -> Unit,
columnActions: List<(List<String>) -> Unit>
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
.combinedClickable(onClick = onClick, onLongClick = onLongClick),
horizontalArrangement = Arrangement.SpaceEvenly
) {
values.forEachIndexed { index, value ->
val sdf = SimpleDateFormat(stringResource(R.string.dateFormat), Locale.getDefault())
val currentDate = sdf.format(Date())
val context = LocalContext.current // Accediendo al contexto
val customOrange = ContextCompat.getColor(context, R.color.verdnatura_pumpkin_orange)
val customWhite = ContextCompat.getColor(context, R.color.verdnatura_white)
val color = if (value == currentDate) Color(customOrange) else Color(customWhite)
Text(
text = value,
color = color,
textAlign = TextAlign.Center,
modifier = Modifier
.weight(1f)
.then(if (index < columnActions.size) Modifier.clickable {
columnActions[index](values)
} else Modifier)
.align(Alignment.CenterVertically)
.padding(4.dp)
)
}
}
}
@Composable
fun TextDayOfSale(
values: List<String>,
value: String,
index: Int,
columnActions: List<(List<String>) -> Unit>
) {
val sdf = SimpleDateFormat(stringResource(R.string.dateFormat), Locale.getDefault())
val currentDate = sdf.format(Date())
val context = LocalContext.current
val customOrange = ContextCompat.getColor(context, R.color.verdnatura_pumpkin_orange)
val customWhite = ContextCompat.getColor(context, R.color.verdnatura_white)
val color = if (value == currentDate) Color(customOrange) else Color(customWhite)
Text(
text = value,
color = color,
modifier = Modifier
.then(if (index < columnActions.size) Modifier.clickable {
columnActions[index](values)
} else Modifier)
)
}

View File

@ -0,0 +1,82 @@
package es.verdnatura.presentation.view.feature.paletizador.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
import androidx.activity.OnBackPressedDispatcher
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import es.verdnatura.R
import es.verdnatura.presentation.common.OnBackPressedListener
import es.verdnatura.presentation.view.component.CustomDialogListComposable
import es.verdnatura.presentation.view.feature.main.activity.MainActivity
import org.koin.androidx.viewmodel.ext.android.viewModel
class ExpeditionScanSorterFragmentCompose(
var title: String = ""
) : Fragment(), OnBackPressedListener {
private val viewModel: ExpeditionScanSorterViewModelCompose by viewModel()
private lateinit var backDispatcher: OnBackPressedDispatcher
companion object {
fun newInstance(title: String) = ExpeditionScanSorterFragmentCompose(title)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
val composeView = ComposeView(requireContext())
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
(activity as? MainActivity)?.onMyBackPressed()
}
})
composeView.setContent {
SetComposableView()
}
return composeView
}
@Composable
private fun SetComposableView() {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
CustomDialogListComposable(
title = getString(R.string.expeditionNoSorter),
onOkClick = { (context as MainActivity).onMyBackPressed() },
showRecyclerView = true,
buttonOkText = getString(R.string.end),
recyclerViewItems = uiState.items,
onEnterPressed = { text ->
if (text.isNotEmpty()) try {
viewModel.expeditionScan(text.toLong())
} catch (ex: Exception) {
(context as MainActivity).messageWithSound(
message = getString(R.string.errorInput),
isPlayed = true,
isToasted = true,
isError = true
)
}
}
)
}
override fun onBackPressedHandled(): Boolean {
return true
}
override fun onDestroyView() {
super.onDestroyView()
(activity as? MainActivity)?.onMyBackPressed()
}
}

View File

@ -0,0 +1,44 @@
package es.verdnatura.presentation.view.feature.paletizador.fragment
import es.verdnatura.domain.SalixCallback
import es.verdnatura.presentation.base.BaseViewModelCompose
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import retrofit2.Response
data class UiState(
var items: MutableList<Number> = mutableListOf(),
val isLoading: Boolean = false
)
class ExpeditionScanSorterViewModelCompose(var application: android.app.Application) :
BaseViewModelCompose(application) {
private val _uiState = MutableStateFlow(UiState())
val uiState = _uiState.asStateFlow()
fun expeditionScan(vExpeditionId: Number) {
salix.expeditionScan(params = arrayListOf(vExpeditionId))
.enqueue(object : SalixCallback<Unit>(application.applicationContext) {
override fun onSuccess(response: Response<Unit>) {
super.onSuccess(response)
_uiState.update { currentState ->
currentState.copy(
items = currentState.items.toMutableList().apply { add(vExpeditionId) },
isLoading = false
)
}
}
override fun onError(t: Throwable) {
super.onError(t)
_uiState.update { it.copy(isLoading = false) }
}
})
}
}

View File

@ -41,7 +41,7 @@
<dimen name="item_list_separation">4dp</dimen>
<!--text toolbar-->
<dimen name="title">18sp</dimen>
<dimen name="title">20sp</dimen>
<dimen name="subtitle">16sp</dimen>
<!--Text sizes-->