diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/adapters/CategoryItemAdapter.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/adapters/CategoryItemAdapter.kt deleted file mode 100644 index 2912d6c4..00000000 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/adapters/CategoryItemAdapter.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.hieuwu.groceriesstore.presentation.adapters - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import com.hieuwu.groceriesstore.databinding.LayoutCategoryGridItemBinding -import com.hieuwu.groceriesstore.domain.models.CategoryModel - -class CategoryItemAdapter(val onClickListener: OnClickListener) : - ListAdapter(DiffCallback) { - - class CategoryItemViewHolder(private var binding: LayoutCategoryGridItemBinding) : - RecyclerView.ViewHolder(binding.root) { - fun bind(category: CategoryModel) { - binding.category = category - binding.executePendingBindings() - } - } - - override fun onBindViewHolder(holder: CategoryItemViewHolder, position: Int) { - val category = getItem(position) - holder.itemView.setOnClickListener { - onClickListener.onClick(category) - } - holder.bind(category) - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryItemViewHolder { - return CategoryItemViewHolder( - LayoutCategoryGridItemBinding.inflate( - LayoutInflater.from( - parent.context - ) - ) - ) - } - - companion object DiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: CategoryModel, newItem: CategoryModel): Boolean { - return oldItem == newItem - } - - override fun areContentsTheSame(oldItem: CategoryModel, newItem: CategoryModel): Boolean { - return oldItem.id == newItem.id - } - } - - class OnClickListener(val clickListener: (category: CategoryModel) -> Unit) { - fun onClick(category: CategoryModel) = clickListener(category) - } -} diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/adapters/GridListItemAdapter.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/adapters/GridListItemAdapter.kt deleted file mode 100644 index da280f0a..00000000 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/adapters/GridListItemAdapter.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.hieuwu.groceriesstore.presentation.adapters - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import com.hieuwu.groceriesstore.R -import com.hieuwu.groceriesstore.databinding.LayoutGridListItemBinding -import com.hieuwu.groceriesstore.domain.models.ProductModel - -class GridListItemAdapter(private val onClickListener: OnClickListener) : - ListAdapter(DiffCallback) { - - class ProductGridViewHolder(private val binding: LayoutGridListItemBinding) : - RecyclerView.ViewHolder(binding.root) { - fun bind(productModel: ProductModel) { - binding.product = productModel - binding.executePendingBindings() - } - } - - override fun onBindViewHolder(holder: ProductGridViewHolder, position: Int) { - val product = getItem(position) - holder.itemView.setOnClickListener { - onClickListener.onClick(product) - } - - holder.itemView.findViewById(R.id.product_add_button).setOnClickListener { - onClickListener.addToCartListener(product) - } - - holder.bind(product) - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductGridViewHolder { - val binding = LayoutGridListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return ProductGridViewHolder(binding) - } - - companion object DiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: ProductModel, newItem: ProductModel): Boolean { - return oldItem == newItem - } - - override fun areContentsTheSame(oldItem: ProductModel, newItem: ProductModel): Boolean { - return oldItem.id == newItem.id - } - } - - class OnClickListener( - val clickListener: (product: ProductModel) -> Unit, - val addToCartListener: (product: ProductModel) -> Unit - ) { - fun onClick(product: ProductModel) = clickListener(product) - fun onAddButtonClick(product: ProductModel) = addToCartListener(product) - } -} diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/ExploreFragment.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/ExploreFragment.kt index b281616d..22c3a65c 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/ExploreFragment.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/ExploreFragment.kt @@ -1,76 +1,36 @@ package com.hieuwu.groceriesstore.presentation.explore -import android.annotation.SuppressLint -import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.View -import android.view.View.* import android.view.ViewGroup -import android.widget.EditText -import androidx.appcompat.widget.AppCompatImageView -import androidx.databinding.DataBindingUtil +import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController -import com.hieuwu.groceriesstore.R -import com.hieuwu.groceriesstore.databinding.FragmentExploreBinding -import com.hieuwu.groceriesstore.presentation.adapters.CategoryItemAdapter -import com.hieuwu.groceriesstore.presentation.adapters.GridListItemAdapter import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch -import timber.log.Timber @AndroidEntryPoint class ExploreFragment : Fragment() { - private lateinit var binding: FragmentExploreBinding - private val viewModel: ExploreViewModel by viewModels() - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - binding = DataBindingUtil.inflate( - inflater, R.layout.fragment_explore, container, false - ) - binding.viewModel = viewModel - binding.lifecycleOwner = this - - initAdapters() - setObserver() - setTextSearchColor() - setEventListener() - - return binding.root - } - - private fun initAdapters() { - binding.productRecyclerview.adapter = GridListItemAdapter( - createGridListItemEvent() - ) - - binding.categoryRecyclerview.adapter = - CategoryItemAdapter(CategoryItemAdapter.OnClickListener { - navigateToProductList(it.name!!, it.id) - }) - } - - private fun createGridListItemEvent(): GridListItemAdapter.OnClickListener = - GridListItemAdapter.OnClickListener( - clickListener = { - viewModel.displayProductDetail(it) - viewModel.displayProductDetailComplete() - }, - addToCartListener = { - viewModel.addToCart(it) + return ComposeView(requireContext()).apply { + setContent { + ExploreScreen( + navigateToProductList = { category -> + navigateToProductList(category.id, category.name!!) + }, + navigateToProductDetail = { product -> + navigateToProductDetail(product.id) + } + ) } - ) + } + } private fun navigateToProductList(name: String, id: String) { val direction = @@ -83,75 +43,4 @@ class ExploreFragment : Fragment() { ExploreFragmentDirections.actionExploreFragmentToProductDetailFragment(productId) findNavController().navigate(direction) } - - @SuppressLint("UnsafeRepeatOnLifecycleDetector") - private fun setObserver() { - viewModel.navigateToSelectedProperty.observe(viewLifecycleOwner) { - if (null != it) navigateToProductDetail(it.id) - } - viewModel.productList.observe(viewLifecycleOwner) { - if (it.isEmpty()) { - Timber.d("Empty") - } else { - binding.productRecyclerview.visibility = VISIBLE - binding.animationLayout.visibility = GONE - Timber.d("Has item") - } - } - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - launch { - viewModel.categories.collect {} - } - - } - } - } - - private fun setTextSearchColor() { - val searchEditText = - binding.searchView.findViewById(androidx.appcompat.R.id.search_src_text) - searchEditText.setTextColor(Color.WHITE) - } - - private fun onTextChange(text: String): Boolean { - viewModel.searchNameChanged(text) - return true - } - - private fun setEventListener() { - binding.searchView.setOnQueryTextListener(object : - androidx.appcompat.widget.SearchView.OnQueryTextListener { - override fun onQueryTextSubmit(query: String?) = onTextChange(query!!) - override fun onQueryTextChange(query: String?) = onTextChange(query!!) - }) - - binding.searchView.setOnQueryTextFocusChangeListener { _, _ -> - Timber.d("Search focused") - // Hide category list - // Show search result list with empty list product - binding.categoryRecyclerview.visibility = GONE - } - - binding.searchView.setOnQueryTextFocusChangeListener { _, _ -> - Timber.d("Search focused") - // Hide category list - // Show search result list with empty list product - binding.categoryRecyclerview.visibility = GONE - } - - val closeSearchButton = - binding.searchView.findViewById(androidx.appcompat.R.id.search_close_btn) - - closeSearchButton.setOnClickListener { - binding.searchView.onActionViewCollapsed() - Timber.d("Close search focused") - // Show category list - // Clear search result - // Hide search result - binding.productRecyclerview.visibility = GONE - binding.categoryRecyclerview.visibility = VISIBLE - binding.animationLayout.visibility = GONE - } - } } diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/ExploreScreen.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/ExploreScreen.kt new file mode 100644 index 00000000..02d77ac8 --- /dev/null +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/ExploreScreen.kt @@ -0,0 +1,256 @@ +package com.hieuwu.groceriesstore.presentation.explore + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.MaterialTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel +import com.airbnb.lottie.compose.LottieAnimation +import com.airbnb.lottie.compose.LottieCompositionSpec +import com.airbnb.lottie.compose.LottieConstants +import com.airbnb.lottie.compose.rememberLottieComposition +import com.hieuwu.groceriesstore.R +import com.hieuwu.groceriesstore.domain.models.CategoryModel +import com.hieuwu.groceriesstore.domain.models.ProductModel +import com.hieuwu.groceriesstore.presentation.explore.composables.CategoryItem +import com.hieuwu.groceriesstore.presentation.explore.composables.ProductItem +import kotlinx.coroutines.launch + +@Composable +fun ExploreScreen( + viewModel: ExploreViewModel = hiltViewModel(), + navigateToProductList: (CategoryModel) -> Unit, + navigateToProductDetail: (ProductModel) -> Unit, +) { + + val searchName by viewModel.searchString.collectAsState() + val categoryList by viewModel.categories.collectAsState() + val productList by viewModel.productList.collectAsState() + + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + + Scaffold( + topBar = { + SearchBarSection( + searchName = searchName, + onSearchNameChange = viewModel::searchNameChanged, + onSearchClick = { + viewModel.searchProduct(searchName) + }, + onClearInput = viewModel::clearInput + ) + }, + snackbarHost = { SnackbarHost(snackbarHostState) } + ) { contentPadding -> + + ProductListSection( + modifier = Modifier.padding(contentPadding), + productList = productList, + categoryList = categoryList, + navigateToProductDetail = navigateToProductDetail, + navigateToProductList = navigateToProductList, + onAddToCartClick = { product -> + viewModel.addToCart(product) + scope.launch { + snackbarHostState.showSnackbar( + "Added ${product.name}" + ) + } + } + ) + } + +} + +@Composable +fun ProductListSection( + modifier: Modifier = Modifier, + categoryList: List?, + productList: List?, + navigateToProductDetail: (ProductModel) -> Unit, + navigateToProductList: (CategoryModel) -> Unit, + onAddToCartClick: (ProductModel) -> Unit +) { + Box(modifier = modifier.fillMaxSize()) { + if (productList == null) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + + val composition by rememberLottieComposition( + LottieCompositionSpec.Asset("explore_animation.json") + ) + LottieAnimation( + composition = composition, + modifier = Modifier.size(200.dp), + iterations = LottieConstants.IterateForever + ) + } + } + else { + + Column { + Text( + text = "Categories", + modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp), + fontWeight = FontWeight.Bold, + fontSize = 20.sp + ) + + LazyRow { + categoryList?.let { + + items(it) { category -> + CategoryItem( + modifier = Modifier.padding(8.dp), + category = category, + onItemClick = { + navigateToProductList(category) + } + ) + } + } + } + + if(productList.isNotEmpty()) { + + Text( + text = "Products", + modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp), + fontWeight = FontWeight.Bold, + fontSize = 20.sp + ) + + LazyVerticalGrid( + columns = GridCells.Fixed(2) + ) { + + items(productList) { product -> + ProductItem( + modifier = Modifier.padding(8.dp), + product = product, + onNavigateToProductDetails = { + navigateToProductDetail(product) + }, + onAddToCartClick = { + onAddToCartClick(product) + } + ) + } + + } + } else { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text( + text = "No Products Found.", + style = MaterialTheme.typography.subtitle1 + ) + } + } + } + } + } +} + +@Composable +fun SearchBarSection( + modifier: Modifier = Modifier, + searchName: String, + onSearchNameChange: (String) -> Unit, + onSearchClick: () -> Unit, + onClearInput: () -> Unit +) { + Box( + modifier = modifier + .fillMaxWidth() + .background(colorResource(id = R.color.colorPrimary)) + .padding(vertical = 12.dp, horizontal = 15.dp) + ) { + + OutlinedTextField( + value = searchName, + onValueChange = onSearchNameChange, + placeholder = { + Text(text = "Search") + }, + modifier = Modifier.fillMaxWidth(), + shape= CircleShape, + colors = TextFieldDefaults.colors( + focusedIndicatorColor = Color.White, + unfocusedIndicatorColor = Color.White, + focusedContainerColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent, + focusedTextColor = Color.White, + unfocusedTextColor = Color.White, + cursorColor = Color.White + ), + leadingIcon = { + Icon( + imageVector = Icons.Filled.Search, + contentDescription = "search_icon", + tint = Color.White + ) + }, + trailingIcon = { + if (searchName.isNotBlank()) { + IconButton(onClick = onClearInput) { + Icon( + painter = painterResource(id = R.drawable.ic_baseline_close), + contentDescription = "search_icon", + tint = Color.White + ) + } + } + }, + keyboardOptions = KeyboardOptions.Default.copy( + imeAction = ImeAction.Search + ), + keyboardActions = KeyboardActions( + onSearch = { + onSearchClick() + } + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/ExploreViewModel.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/ExploreViewModel.kt index 886f7d5a..9993d2db 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/ExploreViewModel.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/ExploreViewModel.kt @@ -1,9 +1,5 @@ package com.hieuwu.groceriesstore.presentation.explore -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -//import androidx.lifecycle.Transformations -import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import com.hieuwu.groceriesstore.data.database.entities.LineItem import com.hieuwu.groceriesstore.data.database.entities.Order @@ -20,9 +16,9 @@ import com.hieuwu.groceriesstore.utilities.OrderStatus import dagger.hilt.android.lifecycle.HiltViewModel import java.util.* import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject @@ -35,6 +31,7 @@ class ExploreViewModel @Inject constructor( private val createNewOrderUseCase: CreateNewOrderUseCase, private val addToCartUseCase: AddToCartUseCase ) : ObservableViewModel() { + private val _currentCart: StateFlow = getCurrentCard()!! .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null) @@ -43,30 +40,21 @@ class ExploreViewModel @Inject constructor( getCategoriesList()!!.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)!! val categories: StateFlow?> get() = _categories - private val searchString: MutableLiveData = MutableLiveData("") - fun searchNameChanged(name: String) { - searchString.value = name - } + private val _productList = MutableStateFlow?>(null) + val productList: StateFlow?> = _productList - //TODO convert this to use flow - val productList: LiveData> =MutableLiveData() - //TODO Handle filtering list after migrate to Supabase -// Transformations.switchMap(searchString) { string -> -// if (string.isNotEmpty()) searchProduct(name = string).asLiveData() -// else MutableLiveData() -// } + private val _searchString = MutableStateFlow("") + val searchString: StateFlow = _searchString - private val _navigateToSelectedProperty = MutableLiveData() - val navigateToSelectedProperty: LiveData - get() = _navigateToSelectedProperty - - fun displayProductDetail(product: ProductModel) { - _navigateToSelectedProperty.value = product + fun searchNameChanged(name: String) { + _searchString.value = name + searchProduct(name) } - fun displayProductDetailComplete() { - _navigateToSelectedProperty.value = null + fun clearInput() { + _searchString.value = "" + _productList.value = null } init { @@ -75,18 +63,15 @@ class ExploreViewModel @Inject constructor( } } - private fun searchProduct(name: String): Flow> { - var output: Flow> = flow {} - viewModelScope.launch { - when (val res = searchProductUseCase.execute(SearchProductUseCase.Input(name = name))) { - is SearchProductUseCase.Output -> { - output = res.result + fun searchProduct(name: String) { + if (name.isNotBlank()) { + viewModelScope.launch { + val res = searchProductUseCase.execute(SearchProductUseCase.Input(name = name.trim())) + res.result.collect { + _productList.value = it } - } - } - return output } private fun getCurrentCard(): Flow? { diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/composables/CategoryItem.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/composables/CategoryItem.kt new file mode 100644 index 00000000..3796623a --- /dev/null +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/composables/CategoryItem.kt @@ -0,0 +1,70 @@ +package com.hieuwu.groceriesstore.presentation.explore.composables + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi +import com.bumptech.glide.integration.compose.GlideImage +import com.hieuwu.groceriesstore.domain.models.CategoryModel + +@OptIn( + ExperimentalGlideComposeApi::class, + ExperimentalMaterialApi::class +) +@Composable +fun CategoryItem( + modifier: Modifier = Modifier, + category: CategoryModel, + onItemClick: () -> Unit +) { + + Card( + onClick = onItemClick, + modifier = modifier, + elevation = 2.dp, + shape = RoundedCornerShape(12.dp) + ) { + + Column( + modifier = modifier + .fillMaxWidth() + .padding(4.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + + GlideImage( + model = category.image, + contentDescription = category.name, + contentScale = ContentScale.Crop, + modifier = modifier + .fillMaxWidth() + .height(80.dp) + .clip(RoundedCornerShape(4.dp)) + ) + + Text( + text = category.name!!, + maxLines = 2, + fontWeight = FontWeight.Bold, + style = MaterialTheme.typography.subtitle1, + textAlign = TextAlign.Center + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/composables/ProductItem.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/composables/ProductItem.kt new file mode 100644 index 00000000..94b13967 --- /dev/null +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/explore/composables/ProductItem.kt @@ -0,0 +1,99 @@ +package com.hieuwu.groceriesstore.presentation.explore.composables + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +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.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Card +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.MaterialTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +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.layout.ContentScale +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.unit.dp +import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi +import com.bumptech.glide.integration.compose.GlideImage +import com.hieuwu.groceriesstore.R +import com.hieuwu.groceriesstore.domain.models.ProductModel + +@OptIn(ExperimentalGlideComposeApi::class, ExperimentalMaterialApi::class) +@Composable +fun ProductItem( + modifier: Modifier = Modifier, + product: ProductModel, + onAddToCartClick: (ProductModel) -> Unit, + onNavigateToProductDetails: (String) -> Unit +) { + Card( + modifier = modifier + .fillMaxWidth(), + elevation = 2.dp, + onClick = { onNavigateToProductDetails(product.id) } + ) { + Column( + modifier = modifier + .fillMaxWidth() + .padding(4.dp), + ) { + GlideImage( + contentScale = ContentScale.Crop, + model = product.image, + contentDescription = null, + modifier = modifier + .fillMaxWidth() + .height(80.dp) + .clip(RoundedCornerShape(4.dp)) + ) + Text( + modifier = modifier.fillMaxWidth(), text = product.name ?: "", + maxLines = 1, + style = MaterialTheme.typography.subtitle1 + ) + Spacer(modifier = modifier.height(4.dp)) + Text( + modifier = modifier.fillMaxWidth(), + text = product.description ?: "", + maxLines = 2, + ) + Spacer(modifier = modifier.height(4.dp)) + Row( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "${product.price}$", + style = MaterialTheme.typography.h6 + ) + Button( + onClick = { onAddToCartClick(product) }, + modifier = Modifier.size(48.dp), + colors = ButtonDefaults.buttonColors( + backgroundColor = colorResource(id = R.color.colorPrimary) + ) + ) { + Icon( + imageVector = Icons.Filled.Add, + contentDescription = null, + tint = Color.White, + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hieuwu/groceriesstore/presentation/utils/BindingAdapters.kt b/app/src/main/java/com/hieuwu/groceriesstore/presentation/utils/BindingAdapters.kt index 5c2f6e85..57475fc6 100644 --- a/app/src/main/java/com/hieuwu/groceriesstore/presentation/utils/BindingAdapters.kt +++ b/app/src/main/java/com/hieuwu/groceriesstore/presentation/utils/BindingAdapters.kt @@ -7,20 +7,11 @@ import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.bumptech.glide.request.RequestOptions import com.hieuwu.groceriesstore.R -import com.hieuwu.groceriesstore.domain.models.CategoryModel import com.hieuwu.groceriesstore.domain.models.LineItemModel -import com.hieuwu.groceriesstore.domain.models.ProductModel import com.hieuwu.groceriesstore.domain.models.RecipeModel -import com.hieuwu.groceriesstore.presentation.adapters.CategoryItemAdapter -import com.hieuwu.groceriesstore.presentation.adapters.GridListItemAdapter import com.hieuwu.groceriesstore.presentation.adapters.LineListItemAdapter import com.hieuwu.groceriesstore.presentation.adapters.RecipeItemAdapter -@BindingAdapter("listData") -fun bindRecyclerView(recyclerView: RecyclerView, data: List?) { - val adapter = recyclerView.adapter as GridListItemAdapter - adapter.submitList(data) -} @BindingAdapter("imageUrl") fun bindImage(imgView: ImageView, imgUrl: String?) { @@ -42,12 +33,6 @@ fun bindRecyclerViewLine(recyclerView: RecyclerView, data: MutableList?) { - val adapter = recyclerView.adapter as CategoryItemAdapter - adapter.submitList(data) -} - @BindingAdapter("recipeListData") fun bindRecyclerViewRecipe(recyclerView: RecyclerView, data: MutableList?) { val adapter = recyclerView.adapter as RecipeItemAdapter diff --git a/app/src/main/res/layout/fragment_explore.xml b/app/src/main/res/layout/fragment_explore.xml deleted file mode 100644 index 4faf3b32..00000000 --- a/app/src/main/res/layout/fragment_explore.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file