package com.example.ui.customer import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border 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.PaddingValues 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.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Star import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState 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.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.example.R import com.example.data.model.FoodItem import com.example.ui.components.FoodItemImage import com.example.ui.components.FoodReviewsBottomSheet import com.example.ui.components.QuantityStepper import com.example.ui.components.VegNonVegBadge import com.example.ui.components.WriteReviewDialog import com.example.ui.theme.AccentGold import com.example.ui.theme.FreshVegGreen import com.example.ui.theme.LoveRedPrimary import com.example.ui.theme.TextPrimary import com.example.ui.theme.TextSecondary import com.example.viewmodel.CustomerNavScreen import com.example.viewmodel.CustomerViewModel import java.util.Locale @Composable fun MenuScreen( customerViewModel: CustomerViewModel, modifier: Modifier = Modifier ) { val allItems by customerViewModel.allFoodItems.collectAsState() val filteredItems by customerViewModel.filteredFoodItems.collectAsState() val selectedCategory by customerViewModel.selectedCategory.collectAsState() val selectedDiet by customerViewModel.selectedDietFilter.collectAsState() val searchQuery by customerViewModel.searchQuery.collectAsState() val cartItems by customerViewModel.cartItems.collectAsState() val allReviews by customerViewModel.allReviews.collectAsState() var selectedDishForReviews by remember { mutableStateOf(null) } var selectedDishForWriteReview by remember { mutableStateOf(null) } // Distinct categories with counts val categoryList = listOf("All") + allItems.map { it.category }.distinct() Box(modifier = modifier.fillMaxSize()) { Column( modifier = Modifier .fillMaxSize() .background(Color(0xFFF8F9FD)) ) { // Top Bar with back button and search Surface( color = Color.White, shadowElevation = 2.dp, modifier = Modifier.fillMaxWidth() ) { Row( modifier = Modifier .fillMaxWidth() .padding(horizontal = 8.dp, vertical = 6.dp), verticalAlignment = Alignment.CenterVertically ) { IconButton( onClick = { customerViewModel.navigateTo(CustomerNavScreen.Home) }, modifier = Modifier.testTag("menu_back_button") ) { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back", tint = TextPrimary ) } OutlinedTextField( value = searchQuery, onValueChange = { customerViewModel.searchQuery.value = it }, placeholder = { Text("Search delicious items...", fontSize = 13.sp, color = Color.Gray) }, leadingIcon = { Icon(Icons.Default.Search, contentDescription = null, tint = LoveRedPrimary, modifier = Modifier.size(18.dp)) }, singleLine = true, shape = RoundedCornerShape(20.dp), colors = OutlinedTextFieldDefaults.colors( focusedTextColor = Color(0xFF1E2022), unfocusedTextColor = Color(0xFF1E2022), focusedContainerColor = Color(0xFFF1F5F9), unfocusedContainerColor = Color(0xFFF1F5F9), cursorColor = LoveRedPrimary, focusedBorderColor = LoveRedPrimary, unfocusedBorderColor = Color.Transparent ), modifier = Modifier .weight(1f) .height(46.dp) .testTag("menu_search_field") ) Spacer(modifier = Modifier.width(8.dp)) Surface( color = FreshVegGreen, shape = RoundedCornerShape(8.dp), modifier = Modifier.padding(end = 4.dp) ) { Row( modifier = Modifier.padding(horizontal = 6.dp, vertical = 4.dp), verticalAlignment = Alignment.CenterVertically ) { Icon(Icons.Default.Star, contentDescription = null, tint = Color.White, modifier = Modifier.size(12.dp)) Spacer(modifier = Modifier.width(2.dp)) Text(text = "4.9", color = Color.White, fontSize = 11.sp, fontWeight = FontWeight.Bold) } } } } // Veg / Non-Veg Filter Chips Surface( color = Color.White, modifier = Modifier.fillMaxWidth() ) { Row( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically ) { Text( text = "Filter: ", fontSize = 12.sp, fontWeight = FontWeight.SemiBold, color = TextSecondary ) DietFilterChip( label = "All (${allItems.size})", isSelected = selectedDiet == "All", onClick = { customerViewModel.selectedDietFilter.value = "All" } ) DietFilterChip( label = "Veg (${allItems.count { it.isVeg }})", isVeg = true, isSelected = selectedDiet == "Veg", onClick = { customerViewModel.selectedDietFilter.value = "Veg" } ) DietFilterChip( label = "Non-Veg (${allItems.count { !it.isVeg }})", isNonVeg = true, isSelected = selectedDiet == "Non-Veg", onClick = { customerViewModel.selectedDietFilter.value = "Non-Veg" } ) } } // Split Layout: Left Category Rail & Right Dishes List Row( modifier = Modifier .fillMaxSize() .padding(bottom = 72.dp) ) { // Left Category Navigation Rail Surface( color = Color.White, modifier = Modifier .width(96.dp) .fillMaxHeight(), border = androidx.compose.foundation.BorderStroke(1.dp, Color(0xFFF1F5F9)) ) { LazyColumn( modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(vertical = 8.dp) ) { items(categoryList) { category -> val count = if (category == "All") allItems.size else allItems.count { it.category == category } val isSelected = selectedCategory == category Column( modifier = Modifier .fillMaxWidth() .clickable { customerViewModel.selectedCategory.value = category } .background(if (isSelected) Color(0xFFFFF1F2) else Color.Transparent) .border( width = if (isSelected) 3.dp else 0.dp, color = if (isSelected) LoveRedPrimary else Color.Transparent ) .padding(vertical = 10.dp, horizontal = 4.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Box( modifier = Modifier .size(38.dp) .clip(CircleShape) .background(if (isSelected) LoveRedPrimary.copy(alpha = 0.15f) else Color(0xFFF8FAFC)), contentAlignment = Alignment.Center ) { Text( text = getCategoryEmoji(category), fontSize = 18.sp ) } Spacer(modifier = Modifier.height(4.dp)) Text( text = category, fontSize = 11.sp, fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Medium, color = if (isSelected) LoveRedPrimary else TextPrimary, maxLines = 2, overflow = TextOverflow.Ellipsis, modifier = Modifier.padding(horizontal = 2.dp) ) Text( text = "$count items", fontSize = 9.sp, color = TextSecondary ) } } } } // Right Items List LazyColumn( modifier = Modifier .weight(1f) .fillMaxHeight() .padding(horizontal = 10.dp), contentPadding = PaddingValues(top = 8.dp, bottom = 48.dp), verticalArrangement = Arrangement.spacedBy(10.dp) ) { if (filteredItems.isEmpty()) { item { Column( modifier = Modifier .fillMaxWidth() .padding(top = 48.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text("No dishes found in this filter", color = TextSecondary, fontSize = 13.sp) } } } else { items(filteredItems) { item -> val qty = cartItems[item.id]?.quantity ?: 0 MenuDishCard( item = item, quantity = qty, onAdd = { customerViewModel.addToCart(item) }, onIncrement = { customerViewModel.addToCart(item) }, onDecrement = { customerViewModel.decrementOrRemoveFromCart(item) }, onReviewClick = { selectedDishForReviews = item } ) } } } } } // Ratings & Written Reviews Bottom Sheet selectedDishForReviews?.let { foodItem -> val dishReviews = allReviews.filter { it.foodItemId == foodItem.id } FoodReviewsBottomSheet( foodItem = foodItem, reviews = dishReviews, onDismiss = { selectedDishForReviews = null }, onWriteReviewClicked = { val target = selectedDishForReviews selectedDishForReviews = null selectedDishForWriteReview = target } ) } // Write Customer Review Dialog selectedDishForWriteReview?.let { foodItem -> WriteReviewDialog( foodItem = foodItem, onDismiss = { selectedDishForWriteReview = null }, onSubmit = { rating, reviewText -> customerViewModel.submitFoodReview(foodItem, rating, reviewText) selectedDishForWriteReview = null } ) } } } @Composable fun DietFilterChip( label: String, isSelected: Boolean, onClick: () -> Unit, isVeg: Boolean = false, isNonVeg: Boolean = false ) { Surface( shape = RoundedCornerShape(16.dp), color = if (isSelected) Color(0xFF1E293B) else Color(0xFFF1F5F9), modifier = Modifier .clickable(onClick = onClick) .testTag("diet_chip_$label") ) { Row( modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp), verticalAlignment = Alignment.CenterVertically ) { if (isVeg) { VegNonVegBadge(isVeg = true, size = 12.dp) Spacer(modifier = Modifier.width(4.dp)) } else if (isNonVeg) { VegNonVegBadge(isVeg = false, size = 12.dp) Spacer(modifier = Modifier.width(4.dp)) } Text( text = label, fontSize = 11.sp, fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Medium, color = if (isSelected) Color.White else TextPrimary ) } } } @Composable fun MenuDishCard( item: FoodItem, quantity: Int, onAdd: () -> Unit, onIncrement: () -> Unit, onDecrement: () -> Unit, onReviewClick: () -> Unit ) { Card( shape = RoundedCornerShape(12.dp), colors = CardDefaults.cardColors(containerColor = Color.White), elevation = CardDefaults.cardElevation(defaultElevation = 1.dp), modifier = Modifier.fillMaxWidth() ) { Column(modifier = Modifier.padding(10.dp)) { // Food item picture Box( modifier = Modifier .fillMaxWidth() .height(110.dp) .clip(RoundedCornerShape(8.dp)) ) { FoodItemImage( imageUri = item.imageUri, contentDescription = item.name, modifier = Modifier.fillMaxSize() ) VegNonVegBadge( isVeg = item.isVeg, modifier = Modifier .align(Alignment.TopStart) .padding(6.dp) ) if (item.isBestSeller) { Surface( color = Color(0xFFFF5722), shape = RoundedCornerShape(bottomEnd = 8.dp), modifier = Modifier.align(Alignment.TopEnd) ) { Text( text = "Trending 🔥", color = Color.White, fontSize = 9.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp) ) } } } Spacer(modifier = Modifier.height(8.dp)) Text( text = item.name, fontWeight = FontWeight.Bold, fontSize = 14.sp, color = TextPrimary, maxLines = 1, overflow = TextOverflow.Ellipsis ) Text( text = item.description, fontSize = 11.sp, color = TextSecondary, maxLines = 1, overflow = TextOverflow.Ellipsis ) Spacer(modifier = Modifier.height(6.dp)) // Rating Badge & Customer Written Reviews Trigger Row( modifier = Modifier .clip(RoundedCornerShape(6.dp)) .background(AccentGold.copy(alpha = 0.12f)) .clickable { onReviewClick() } .padding(horizontal = 6.dp, vertical = 3.dp), verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = Icons.Default.Star, contentDescription = null, tint = AccentGold, modifier = Modifier.size(13.dp) ) Spacer(modifier = Modifier.width(3.dp)) Text( text = String.format(Locale.getDefault(), "%.1f", item.rating), fontWeight = FontWeight.Bold, fontSize = 11.sp, color = TextPrimary ) Spacer(modifier = Modifier.width(4.dp)) Text( text = "(${item.reviewCount} reviews) >", fontSize = 10.sp, color = LoveRedPrimary, fontWeight = FontWeight.SemiBold ) } Spacer(modifier = Modifier.height(6.dp)) Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { Text( text = "₹${item.price.toInt()}", fontWeight = FontWeight.Black, fontSize = 15.sp, color = LoveRedPrimary ) if (quantity > 0) { QuantityStepper( quantity = quantity, onIncrement = onIncrement, onDecrement = onDecrement ) } else { Button( onClick = onAdd, shape = RoundedCornerShape(8.dp), colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFFFF1F2)), border = androidx.compose.foundation.BorderStroke(1.dp, LoveRedPrimary), contentPadding = PaddingValues(horizontal = 14.dp, vertical = 2.dp), modifier = Modifier.height(30.dp).testTag("menu_add_${item.id}") ) { Text( text = "ADD", color = LoveRedPrimary, fontWeight = FontWeight.Black, fontSize = 11.sp ) } } } } } } fun getCategoryEmoji(category: String): String { return when { category.contains("Biryani", ignoreCase = true) -> "🍗" category.contains("Quick", ignoreCase = true) -> "🍟" category.contains("Starter", ignoreCase = true) -> "🍢" category.contains("Burger", ignoreCase = true) -> "🍔" category.contains("Roll", ignoreCase = true) -> "🌯" category.contains("Main", ignoreCase = true) -> "🍛" category.contains("Pasta", ignoreCase = true) -> "🍝" category.contains("Beverage", ignoreCase = true) || category.contains("Shake", ignoreCase = true) -> "🥤" category.contains("Rice", ignoreCase = true) || category.contains("Noodle", ignoreCase = true) -> "🍜" else -> "🍽️" } }