@@ -6,13 +6,26 @@ import androidx.compose.foundation.layout.Arrangement
66import androidx.compose.foundation.layout.Box
77import androidx.compose.foundation.layout.Column
88import androidx.compose.foundation.layout.Row
9+ import androidx.compose.foundation.layout.Spacer
910import androidx.compose.foundation.layout.aspectRatio
1011import androidx.compose.foundation.layout.fillMaxSize
1112import androidx.compose.foundation.layout.fillMaxWidth
1213import androidx.compose.foundation.layout.padding
14+ import androidx.compose.foundation.layout.width
15+ import androidx.compose.material.icons.Icons
16+ import androidx.compose.material.icons.filled.Delete
17+ import androidx.compose.material3.CardDefaults
18+ import androidx.compose.material3.ElevatedCard
19+ import androidx.compose.material3.Icon
20+ import androidx.compose.material3.IconButton
1321import androidx.compose.material3.MaterialTheme
1422import androidx.compose.material3.Text
1523import androidx.compose.runtime.Composable
24+ import androidx.compose.runtime.collectAsState
25+ import androidx.compose.runtime.getValue
26+ import androidx.compose.runtime.mutableStateOf
27+ import androidx.compose.runtime.remember
28+ import androidx.compose.runtime.setValue
1629import androidx.compose.ui.Alignment
1730import androidx.compose.ui.Modifier
1831import androidx.compose.ui.draw.clip
@@ -42,53 +55,145 @@ import java.util.Locale
4255 */
4356@Composable
4457fun ItineraryItemCard (p : Place , placesViewModel : PlacesViewModel , controller : NavHostController ) {
58+ val placesState by placesViewModel.uiState.collectAsState()
59+ val selectedPlace = placesState.selectedPlace
60+ val isActiveRoute = selectedPlace?.id == p.id
4561
46- Row (modifier = Modifier .fillMaxWidth()) {
47- Box (
48- modifier = Modifier
49- .weight(4f )
50- .aspectRatio(1f )
51- .padding(start = 8 .dp, end = 8 .dp, bottom = 8 .dp, top = 2 .dp)
52- .clip(MaterialTheme .shapes.medium)
53- .background(MaterialTheme .colorScheme.secondaryContainer),
54- contentAlignment = Alignment .Center
55- ) {
56- SubcomposeAsyncImage (
57- model = p.imageUrl,
58- contentDescription = " Imagen de ${p.name} " ,
59- contentScale = ContentScale .Crop ,
60- modifier = Modifier .fillMaxSize(),
61- error = {
62- Image (
63- painter = painterResource(R .drawable.ic_picture),
64- contentDescription = null ,
65- colorFilter = ColorFilter .tint(MaterialTheme .colorScheme.onSecondaryContainer),
66- contentScale = ContentScale .Crop
62+ var showReplaceRouteDialog by remember { mutableStateOf(false ) }
63+
64+ if (showReplaceRouteDialog) {
65+ androidx.compose.material3.AlertDialog (
66+ onDismissRequest = {
67+ showReplaceRouteDialog = false
68+ },
69+ title = { Text (" Ruta activa" ) },
70+ text = { Text (" Tienes una ruta activa en curso. ¿Deseas reemplazarla?" ) },
71+ confirmButton = {
72+ androidx.compose.material3.TextButton (
73+ onClick = {
74+ showReplaceRouteDialog = false
75+ placesViewModel.selectForNavigation(p)
76+ controller.navigate(AppScreens .Map .name)
77+ }) {
78+ Text (" Confirmar" )
79+ }
80+ },
81+ dismissButton = {
82+ androidx.compose.material3.TextButton (
83+ onClick = { showReplaceRouteDialog = false }) {
84+ Text (" Cancelar" )
85+ }
86+ })
87+ }
88+
89+ ElevatedCard (
90+ modifier = Modifier .fillMaxWidth(), colors = CardDefaults .elevatedCardColors(
91+ containerColor = if (isActiveRoute) {
92+ MaterialTheme .colorScheme.primaryContainer.copy(alpha = 0.3f )
93+ } else {
94+ MaterialTheme .colorScheme.surfaceVariant.copy(alpha = 0.3f )
95+ }
96+ ),
97+
98+ elevation = CardDefaults .elevatedCardElevation(
99+ defaultElevation = if (isActiveRoute) 4 .dp else 2 .dp
100+ ), shape = MaterialTheme .shapes.medium
101+ ) {
102+ Column {
103+ Row (
104+ modifier = Modifier
105+ .fillMaxWidth()
106+ .padding(12 .dp)
107+ ) {
108+ Box (
109+ modifier = Modifier
110+ .weight(4f )
111+ .aspectRatio(1f )
112+ .clip(MaterialTheme .shapes.medium)
113+ .background(MaterialTheme .colorScheme.secondaryContainer),
114+ contentAlignment = Alignment .Center
115+ ) {
116+ SubcomposeAsyncImage (
117+ model = p.imageUrl,
118+ contentDescription = " Imagen de ${p.name} " ,
119+ contentScale = ContentScale .Crop ,
120+ modifier = Modifier .fillMaxSize(),
121+ error = {
122+ Image (
123+ painter = painterResource(R .drawable.ic_picture),
124+ contentDescription = null ,
125+ colorFilter = ColorFilter .tint(MaterialTheme .colorScheme.onSecondaryContainer),
126+ contentScale = ContentScale .Crop
127+ )
128+ })
129+ }
130+ Spacer (modifier = Modifier .width(12 .dp))
131+ Column (
132+ modifier = Modifier .weight(6f ), verticalArrangement = Arrangement .spacedBy(8 .dp)
133+ ) {
134+ Row (
135+ verticalAlignment = Alignment .CenterVertically ,
136+ horizontalArrangement = Arrangement .spacedBy(8 .dp)
137+ ) {
138+ Text (
139+ text = p.name,
140+ style = MaterialTheme .typography.titleMedium,
141+ color = MaterialTheme .colorScheme.onBackground,
142+ modifier = Modifier .weight(1f , fill = false )
143+ )
144+ if (isActiveRoute) {
145+ Box (
146+ modifier = Modifier
147+ .background(
148+ color = MaterialTheme .colorScheme.primary,
149+ shape = MaterialTheme .shapes.small
150+ )
151+ .padding(horizontal = 8 .dp, vertical = 2 .dp)
152+ ) {
153+ Text (
154+ text = " Activa" ,
155+ style = MaterialTheme .typography.labelSmall,
156+ color = MaterialTheme .colorScheme.onPrimary
157+ )
158+ }
159+ }
160+ }
161+ Text (
162+ text = formatOpenHours(p.openHours),
163+ style = MaterialTheme .typography.bodySmall,
164+ color = MaterialTheme .colorScheme.onBackground
67165 )
68- })
69- }
70- Column (
71- modifier = Modifier
72- .weight(6f )
73- .padding(top = 2 .dp, end = 8 .dp),
74- verticalArrangement = Arrangement .spacedBy(8 .dp)
75- ) {
76- Text (
77- text = p.name,
78- style = MaterialTheme .typography.titleMedium,
79- color = MaterialTheme .colorScheme.onBackground
80- )
81- Text (
82- text = formatOpenHours(p.openHours),
83- style = MaterialTheme .typography.bodySmall,
84- color = MaterialTheme .colorScheme.onBackground
85- )
166+ Row (
167+ modifier = Modifier .fillMaxWidth(),
168+ horizontalArrangement = Arrangement .spacedBy(8 .dp),
169+ verticalAlignment = Alignment .CenterVertically
170+ ) {}
171+ }
172+ }
86173 RumboButton (
87- text = " Iniciar Desplazamiento" , onClick = {
88- placesViewModel.selectForNavigation(p)
89- controller.navigate(AppScreens .Map .name)
90- }, style = RumboButtonStyle .Secondary , icon = painterResource(R .drawable.ic_map)
174+ modifier = Modifier .weight(1f ).fillMaxWidth()
175+ .padding(horizontal = 12 .dp, vertical = 8 .dp),
176+ text = if (isActiveRoute) " Ver Ruta Activa" else " Iniciar Desplazamiento" ,
177+ onClick = {
178+ if (isActiveRoute) {
179+ controller.navigate(AppScreens .Map .name)
180+ } else if (selectedPlace != null ) {
181+ showReplaceRouteDialog = true
182+ } else {
183+ placesViewModel.selectForNavigation(p)
184+ controller.navigate(AppScreens .Map .name)
185+ }
186+ },
187+ style = if (isActiveRoute) RumboButtonStyle .Primary else RumboButtonStyle .Secondary ,
188+ icon = painterResource(R .drawable.ic_map)
91189 )
190+ IconButton (onClick = { placesViewModel.removeFromItinerary(p) }) {
191+ Icon (
192+ imageVector = Icons .Default .Delete ,
193+ contentDescription = " Eliminar del Itinerario" ,
194+ tint = MaterialTheme .colorScheme.error
195+ )
196+ }
92197 }
93198 }
94199
@@ -103,7 +208,9 @@ fun ItineraryItemCard(p: Place, placesViewModel: PlacesViewModel, controller: Na
103208 * - Si hay un rango mas tarde hoy: "Cerrado ahora. Abre hoy a ...".
104209 * - Si no hay mas rangos hoy: busca el proximo dia con apertura.
105210 */
106- fun formatOpenHours (openHours : List <String >? , now : LocalDateTime = LocalDateTime .now()): String {
211+ fun formatOpenHours (
212+ openHours : List <String >? , now : LocalDateTime = LocalDateTime .now()
213+ ): String {
107214 if (openHours.isNullOrEmpty()) {
108215 return " No hay información de horario"
109216 }
0 commit comments