diff --git a/dripzone-app/android/app/src/main/AndroidManifest.xml b/dripzone-app/android/app/src/main/AndroidManifest.xml index 43b150e..25774f4 100644 --- a/dripzone-app/android/app/src/main/AndroidManifest.xml +++ b/dripzone-app/android/app/src/main/AndroidManifest.xml @@ -28,6 +28,20 @@ android:theme="@style/Theme.DripZone" tools:targetApi="31"> + + + + + + + + + - - - - - - - + android:exported="false" /> diff --git a/dripzone-app/android/app/src/main/java/co/dripzone/app/MainActivity.kt b/dripzone-app/android/app/src/main/java/co/dripzone/app/MainActivity.kt index 30471d9..69301c4 100644 --- a/dripzone-app/android/app/src/main/java/co/dripzone/app/MainActivity.kt +++ b/dripzone-app/android/app/src/main/java/co/dripzone/app/MainActivity.kt @@ -1,19 +1,25 @@ package co.dripzone.app +import android.content.Context import android.os.Bundle import android.text.Editable import android.text.TextWatcher import android.view.View import android.view.inputmethod.InputMethodManager import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import co.dripzone.app.api.ApiClient import co.dripzone.app.databinding.ActivityMainBinding +import co.dripzone.app.model.CartManager import co.dripzone.app.model.Category +import co.dripzone.app.model.City import co.dripzone.app.model.Product import co.dripzone.app.security.SecurityManager +import co.dripzone.app.ui.CartBottomSheet import co.dripzone.app.ui.CategoryAdapter +import co.dripzone.app.ui.CitySelectorBottomSheet import co.dripzone.app.ui.CustomOrderBottomSheet import co.dripzone.app.ui.ProductAdapter import co.dripzone.app.ui.ProductDetailBottomSheet @@ -28,20 +34,39 @@ class MainActivity : AppCompatActivity() { private lateinit var categoryAdapter: CategoryAdapter private var allProducts: List = emptyList() + private var availableCities: List = City.defaultCities() + private var selectedCity: City = City.defaultCities().first() private var selectedCategory: String = "Todos" private var searchQuery: String = "" + private val cartListener = { + updateCartBadges() + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) + CartManager.init(this) + CartManager.addListener(cartListener) + + loadSavedCity() setupRecyclerViews() setupCategories() setupSearch() - setupListeners() + setupCitySelector() + setupBottomDock() + setupHeaderListeners() + updateCartBadges() loadProducts(forceRefresh = false) + loadCities() + } + + override fun onDestroy() { + super.onDestroy() + CartManager.removeListener(cartListener) } private fun setupRecyclerViews() { @@ -51,21 +76,8 @@ class MainActivity : AppCompatActivity() { .show(supportFragmentManager, ProductDetailBottomSheet.TAG) }, onQuickOrderClick = { product -> - val whatsappMsg = buildString { - append("¡Hola DripZone! 🔥 Quiero pedir este producto con *Pago Contraentrega*:\n\n") - append("👟 *${product.name}*\n") - append("🏷️ *Marca:* ${product.brand}\n") - append("💰 *Precio:* ${product.formattedPrice()}\n") - if (product.firstImage().isNotBlank()) { - append("🔗 *Foto:* ${product.firstImage()}\n") - } - append("\n¿Tienen disponibilidad para envío inmediato?") - } - SecurityManager.openWhatsApp( - this, - SecurityManager.WHATSAPP_STORE_PHONE, - whatsappMsg - ) + ProductDetailBottomSheet.newInstance(product) + .show(supportFragmentManager, ProductDetailBottomSheet.TAG) } ) @@ -96,15 +108,7 @@ class MainActivity : AppCompatActivity() { private fun setupSearch() { binding.btnToggleSearch.setOnClickListener { - if (binding.layoutSearchBar.visibility == View.VISIBLE) { - binding.layoutSearchBar.visibility = View.GONE - binding.etSearch.text?.clear() - hideKeyboard() - } else { - binding.layoutSearchBar.visibility = View.VISIBLE - binding.etSearch.requestFocus() - showKeyboard() - } + toggleSearch() } binding.btnClearSearch.setOnClickListener { @@ -142,28 +146,170 @@ class MainActivity : AppCompatActivity() { } } - private fun setupListeners() { - // Swipe to Refresh: fetches newest drops published from WhatsApp supplier groups - binding.swipeRefresh.setColorSchemeResources(R.color.accent_green) + private fun toggleSearch() { + if (binding.layoutSearchBar.visibility == View.VISIBLE) { + binding.layoutSearchBar.visibility = View.GONE + binding.etSearch.text?.clear() + hideKeyboard() + } else { + binding.layoutSearchBar.visibility = View.VISIBLE + binding.etSearch.requestFocus() + showKeyboard() + } + } + + private fun setupCitySelector() { + updateCityDisplay() + binding.btnChangeCityBar.setOnClickListener { + openCitySelector() + } + } + + private fun openCitySelector() { + CitySelectorBottomSheet.newInstance( + cities = availableCities, + selectedCity = selectedCity + ) { city -> + selectedCity = city + saveSelectedCity(city) + updateCityDisplay() + }.show(supportFragmentManager, CitySelectorBottomSheet.TAG) + } + + private fun updateCityDisplay() { + val freeExchangeText = if (selectedCity.firstExchangeFree) " • 1er Cambio Gratis" else "" + binding.tvHeaderCityInfo.text = "Enviando a ${selectedCity.name} • ${selectedCity.deliveryTime}$freeExchangeText" + } + + private fun loadSavedCity() { + try { + val prefs = SecurityManager.getSecurePreferences(this) + val cityName = prefs.getString("saved_city_name", null) + if (!cityName.isNullOrBlank()) { + val found = availableCities.find { it.name.equals(cityName, ignoreCase = true) } + if (found != null) { + selectedCity = found + } + } + } catch (e: Exception) { + // Ignore + } + } + + private fun saveSelectedCity(city: City) { + try { + val prefs = SecurityManager.getSecurePreferences(this) + prefs.edit().putString("saved_city_name", city.name).apply() + } catch (e: Exception) { + // Ignore + } + } + + private fun setupBottomDock() { + // 1. Inicio + binding.navBtnHome.setOnClickListener { + setDockActive("home") + binding.rvProducts.smoothScrollToPosition(0) + } + + // 2. Buscar + binding.navBtnSearch.setOnClickListener { + setDockActive("search") + toggleSearch() + } + + // 3. Carrito + binding.navBtnCart.setOnClickListener { + openCart() + } + + // 4. Encargos + binding.navBtnEncargos.setOnClickListener { + CustomOrderBottomSheet.newInstance() + .show(supportFragmentManager, CustomOrderBottomSheet.TAG) + } + + // 5. WhatsApp Asesor + binding.navBtnWhatsApp.setOnClickListener { + openGeneralWhatsApp() + } + } + + private fun setDockActive(tab: String) { + val fuchsia = ContextCompat.getColor(this, R.color.drip_fuchsia) + val muted = ContextCompat.getColor(this, R.color.text_secondary) + + binding.ivNavHome.setColorFilter(if (tab == "home") fuchsia else muted) + binding.tvNavHome.setTextColor(if (tab == "home") fuchsia else muted) + + binding.ivNavSearch.setColorFilter(if (tab == "search") fuchsia else muted) + binding.tvNavSearch.setTextColor(if (tab == "search") fuchsia else muted) + } + + private fun setupHeaderListeners() { + // Header Cart icon + binding.btnHeaderCart.setOnClickListener { + openCart() + } + + // Header WhatsApp button + binding.btnHeaderWhatsApp.setOnClickListener { + openGeneralWhatsApp() + } + + // SwipeRefresh: pulls latest live drops from suppliers + binding.swipeRefresh.setColorSchemeResources(R.color.drip_fuchsia, R.color.drip_purple) binding.swipeRefresh.setProgressBackgroundColorSchemeResource(R.color.bg_card) binding.swipeRefresh.setOnRefreshListener { loadProducts(forceRefresh = true) } + } - // WhatsApp direct chat from header - binding.btnHeaderWhatsApp.setOnClickListener { - val generalInquiry = "¡Hola DripZone! 🔥 Me comunico desde la aplicación móvil. Quiero información sobre los productos y envíos contraentrega." - SecurityManager.openWhatsApp( - this, - SecurityManager.WHATSAPP_STORE_PHONE, - generalInquiry - ) + private fun openCart() { + CartBottomSheet.newInstance(selectedCity) { + openCitySelector() + }.show(supportFragmentManager, CartBottomSheet.TAG) + } + + private fun openGeneralWhatsApp() { + val inquiry = "¡Hola DripZone! 🔥 Me comunico desde la app móvil. Quiero información sobre los productos disponibles para entrega contraentrega en ${selectedCity.name}." + SecurityManager.openWhatsApp( + this, + SecurityManager.WHATSAPP_STORE_PHONE, + inquiry + ) + } + + private fun updateCartBadges() { + val count = CartManager.getTotalCount() + if (count > 0) { + binding.tvHeaderCartBadge.visibility = View.VISIBLE + binding.tvHeaderCartBadge.text = count.toString() + + binding.tvBottomCartBadge.visibility = View.VISIBLE + binding.tvBottomCartBadge.text = count.toString() + } else { + binding.tvHeaderCartBadge.visibility = View.GONE + binding.tvBottomCartBadge.visibility = View.GONE } + } - // Floating Action Button: Encargos Especiales - binding.fabCustomOrder.setOnClickListener { - CustomOrderBottomSheet.newInstance() - .show(supportFragmentManager, CustomOrderBottomSheet.TAG) + private fun loadCities() { + lifecycleScope.launch { + try { + val fetched = ApiClient.getCities(this@MainActivity) + if (fetched.isNotEmpty()) { + availableCities = fetched + val currentCityId = selectedCity.id + val matched = availableCities.find { it.id == currentCityId } + if (matched != null) { + selectedCity = matched + } + updateCityDisplay() + } + } catch (e: Exception) { + // Keep default + } } } @@ -181,17 +327,17 @@ class MainActivity : AppCompatActivity() { if (forceRefresh) { Snackbar.make( binding.rootCoordinator, - "Catálogo actualizado con los últimos drops", + "Catálogo sincronizado con los últimos drops de WhatsApp", Snackbar.LENGTH_SHORT ).setBackgroundTint(getColor(R.color.bg_card_elevated)) - .setTextColor(getColor(R.color.accent_green)) + .setTextColor(getColor(R.color.drip_fuchsia)) .show() } } catch (e: Exception) { Snackbar.make( binding.rootCoordinator, - "Cargando catálogo en modo sin conexión", - Snackbar.LENGTH_LONG + "Catálogo cargado en modo sin conexión", + Snackbar.LENGTH_SHORT ).show() } finally { binding.progressBar.visibility = View.GONE @@ -242,12 +388,12 @@ class MainActivity : AppCompatActivity() { } private fun showKeyboard() { - val imm = getSystemService(INPUT_METHOD_SERVICE) as? InputMethodManager + val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager imm?.showSoftInput(binding.etSearch, InputMethodManager.SHOW_IMPLICIT) } private fun hideKeyboard() { - val imm = getSystemService(INPUT_METHOD_SERVICE) as? InputMethodManager + val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager imm?.hideSoftInputFromWindow(binding.etSearch.windowToken, 0) } } diff --git a/dripzone-app/android/app/src/main/java/co/dripzone/app/SplashActivity.kt b/dripzone-app/android/app/src/main/java/co/dripzone/app/SplashActivity.kt new file mode 100644 index 0000000..60b0e4a --- /dev/null +++ b/dripzone-app/android/app/src/main/java/co/dripzone/app/SplashActivity.kt @@ -0,0 +1,29 @@ +package co.dripzone.app + +import android.annotation.SuppressLint +import android.content.Intent +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import androidx.appcompat.app.AppCompatActivity +import co.dripzone.app.databinding.ActivitySplashBinding + +@SuppressLint("CustomSplashScreen") +class SplashActivity : AppCompatActivity() { + + private lateinit var binding: ActivitySplashBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySplashBinding.inflate(layoutInflater) + setContentView(binding.root) + + // Smooth 1.2s delay to showcase DripZone clean splash screen branding + Handler(Looper.getMainLooper()).postDelayed({ + val intent = Intent(this@SplashActivity, MainActivity::class.java) + startActivity(intent) + finish() + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out) + }, 1200) + } +} diff --git a/dripzone-app/android/app/src/main/java/co/dripzone/app/api/ApiClient.kt b/dripzone-app/android/app/src/main/java/co/dripzone/app/api/ApiClient.kt index ff88b93..9f29d4a 100644 --- a/dripzone-app/android/app/src/main/java/co/dripzone/app/api/ApiClient.kt +++ b/dripzone-app/android/app/src/main/java/co/dripzone/app/api/ApiClient.kt @@ -162,4 +162,32 @@ object ApiClient { ) ) } + + suspend fun getCities(context: Context): List = + withContext(Dispatchers.IO) { + try { + val request = Request.Builder() + .url("https://dripzone.almeira.com.co/api/cities") + .header("Accept", "application/json") + .build() + val response = client.newCall(request).execute() + if (response.isSuccessful) { + val body = response.body?.string() + if (!body.isNullOrBlank()) { + val obj = gson.fromJson(body, com.google.gson.JsonObject::class.java) + if (obj.has("cities")) { + val listType = object : TypeToken>() {}.type + val list: List? = gson.fromJson(obj.get("cities"), listType) + if (!list.isNullOrEmpty()) { + return@withContext list + } + } + } + } + } catch (e: Exception) { + Log.w(TAG, "Error fetching cities from API: ${e.message}") + } + co.dripzone.app.model.City.defaultCities() + } } + diff --git a/dripzone-app/android/app/src/main/java/co/dripzone/app/model/CartItem.kt b/dripzone-app/android/app/src/main/java/co/dripzone/app/model/CartItem.kt new file mode 100644 index 0000000..0caee85 --- /dev/null +++ b/dripzone-app/android/app/src/main/java/co/dripzone/app/model/CartItem.kt @@ -0,0 +1,25 @@ +package co.dripzone.app.model + +import java.io.Serializable +import java.text.NumberFormat +import java.util.Locale + +data class CartItem( + val product: Product, + val selectedSize: String, + var quantity: Int = 1 +) : Serializable { + + val itemTotal: Double + get() = product.effectivePrice * quantity + + fun formattedItemTotal(): String { + return try { + val format = NumberFormat.getCurrencyInstance(Locale("es", "CO")) + format.maximumFractionDigits = 0 + format.format(itemTotal) + " COP" + } catch (e: Exception) { + "$ ${itemTotal.toLong()} COP" + } + } +} diff --git a/dripzone-app/android/app/src/main/java/co/dripzone/app/model/CartManager.kt b/dripzone-app/android/app/src/main/java/co/dripzone/app/model/CartManager.kt new file mode 100644 index 0000000..071e200 --- /dev/null +++ b/dripzone-app/android/app/src/main/java/co/dripzone/app/model/CartManager.kt @@ -0,0 +1,119 @@ +package co.dripzone.app.model + +import android.content.Context +import co.dripzone.app.security.SecurityManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import java.text.NumberFormat +import java.util.Locale + +object CartManager { + + private const val CART_PREFS_KEY = "dripzone_cart_items" + private val cartItems = mutableListOf() + private val listeners = mutableListOf<() -> Unit>() + private val gson = Gson() + private var initialized = false + + fun init(context: Context) { + if (initialized) return + initialized = true + try { + val prefs = SecurityManager.getSecurePreferences(context) + val json = prefs.getString(CART_PREFS_KEY, null) + if (!json.isNullOrBlank()) { + val type = object : TypeToken>() {}.type + val loaded: List? = gson.fromJson(json, type) + if (loaded != null) { + cartItems.clear() + cartItems.addAll(loaded) + } + } + } catch (e: Exception) { + // Safe fallback + } + } + + private fun persist(context: Context) { + try { + val prefs = SecurityManager.getSecurePreferences(context) + val json = gson.toJson(cartItems) + prefs.edit().putString(CART_PREFS_KEY, json).apply() + } catch (e: Exception) { + // Ignore + } + } + + fun addItem(context: Context, product: Product, size: String, quantity: Int = 1) { + init(context) + val existingIndex = cartItems.indexOfFirst { + it.product.id == product.id && it.selectedSize == size + } + if (existingIndex >= 0) { + cartItems[existingIndex].quantity += quantity + } else { + cartItems.add(CartItem(product = product, selectedSize = size, quantity = quantity)) + } + persist(context) + notifyListeners() + } + + fun removeItem(context: Context, productId: String, size: String) { + init(context) + cartItems.removeAll { it.product.id == productId && it.selectedSize == size } + persist(context) + notifyListeners() + } + + fun updateQuantity(context: Context, productId: String, size: String, delta: Int) { + init(context) + val index = cartItems.indexOfFirst { it.product.id == productId && it.selectedSize == size } + if (index >= 0) { + val newQty = cartItems[index].quantity + delta + if (newQty <= 0) { + cartItems.removeAt(index) + } else { + cartItems[index].quantity = newQty + } + persist(context) + notifyListeners() + } + } + + fun clear(context: Context) { + cartItems.clear() + persist(context) + notifyListeners() + } + + fun getItems(): List = cartItems.toList() + + fun getTotalCount(): Int = cartItems.sumOf { it.quantity } + + fun getSubtotal(): Double = cartItems.sumOf { it.itemTotal } + + fun formattedSubtotal(): String { + val total = getSubtotal() + return try { + val format = NumberFormat.getCurrencyInstance(Locale("es", "CO")) + format.maximumFractionDigits = 0 + format.format(total) + " COP" + } catch (e: Exception) { + "$ ${total.toLong()} COP" + } + } + + fun addListener(listener: () -> Unit) { + if (!listeners.contains(listener)) { + listeners.add(listener) + } + } + + fun removeListener(listener: () -> Unit) { + listeners.remove(listener) + } + + private fun notifyListeners() { + listeners.forEach { it.invoke() } + } +} diff --git a/dripzone-app/android/app/src/main/java/co/dripzone/app/model/City.kt b/dripzone-app/android/app/src/main/java/co/dripzone/app/model/City.kt new file mode 100644 index 0000000..923d153 --- /dev/null +++ b/dripzone-app/android/app/src/main/java/co/dripzone/app/model/City.kt @@ -0,0 +1,122 @@ +package co.dripzone.app.model + +import com.google.gson.annotations.SerializedName +import java.io.Serializable +import java.text.NumberFormat +import java.util.Locale + +data class City( + @SerializedName("id") + val id: String = "", + + @SerializedName("name") + val name: String = "", + + @SerializedName("department") + val department: String = "", + + @SerializedName("shippingCost") + val shippingCost: Double = 0.0, + + @SerializedName("deliveryTime") + val deliveryTime: String = "24-48 horas", + + @SerializedName("allowsContraentrega") + val allowsContraentrega: Boolean = true, + + @SerializedName("firstExchangeFree") + val firstExchangeFree: Boolean = false, + + @SerializedName("isActive") + val isActive: Boolean = true +) : Serializable { + + fun formattedShipping(): String { + return if (shippingCost <= 0) { + "Envío GRATIS" + } else { + val format = NumberFormat.getCurrencyInstance(Locale("es", "CO")) + format.maximumFractionDigits = 0 + format.format(shippingCost) + " COP" + } + } + + companion object { + fun defaultCities(): List { + return listOf( + City( + id = "cali", + name = "Cali", + department = "Valle del Cauca", + shippingCost = 0.0, + deliveryTime = "Mismo día / 24h", + allowsContraentrega = true, + firstExchangeFree = true + ), + City( + id = "bogota", + name = "Bogotá", + department = "Cundinamarca", + shippingCost = 15000.0, + deliveryTime = "1 a 2 días", + allowsContraentrega = true, + firstExchangeFree = false + ), + City( + id = "medellin", + name = "Medellín", + department = "Antioquia", + shippingCost = 15000.0, + deliveryTime = "1 a 2 días", + allowsContraentrega = true, + firstExchangeFree = false + ), + City( + id = "barranquilla", + name = "Barranquilla", + department = "Atlántico", + shippingCost = 18000.0, + deliveryTime = "2 a 3 días", + allowsContraentrega = true, + firstExchangeFree = false + ), + City( + id = "bucaramanga", + name = "Bucaramanga", + department = "Santander", + shippingCost = 16000.0, + deliveryTime = "2 a 3 días", + allowsContraentrega = true, + firstExchangeFree = false + ), + City( + id = "pereira", + name = "Pereira / Eje Cafetero", + department = "Risaralda", + shippingCost = 14000.0, + deliveryTime = "24 a 48 horas", + allowsContraentrega = true, + firstExchangeFree = false + ), + City( + id = "cartagena", + name = "Cartagena", + department = "Bolívar", + shippingCost = 18000.0, + deliveryTime = "2 a 3 días", + allowsContraentrega = true, + firstExchangeFree = false + ), + City( + id = "otra", + name = "Otro municipio de Colombia", + department = "Nacional", + shippingCost = 18000.0, + deliveryTime = "2 a 4 días", + allowsContraentrega = true, + firstExchangeFree = false + ) + ) + } + } +} diff --git a/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/CartAdapter.kt b/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/CartAdapter.kt new file mode 100644 index 0000000..521d769 --- /dev/null +++ b/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/CartAdapter.kt @@ -0,0 +1,75 @@ +package co.dripzone.app.ui + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import co.dripzone.app.R +import co.dripzone.app.databinding.ItemCartItemBinding +import co.dripzone.app.model.CartItem +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions + +class CartAdapter( + private val items: MutableList, + private val onQuantityChanged: (CartItem, Int) -> Unit, + private val onItemRemoved: (CartItem) -> Unit +) : RecyclerView.Adapter() { + + inner class CartViewHolder(private val binding: ItemCartItemBinding) : + RecyclerView.ViewHolder(binding.root) { + + fun bind(item: CartItem) { + val context = binding.root.context + + binding.tvCartName.text = item.product.name + binding.tvCartSize.text = "Talla: ${item.selectedSize}" + binding.tvCartPrice.text = item.formattedItemTotal() + binding.tvQuantity.text = item.quantity.toString() + + val imageUrl = item.product.firstImage() + if (imageUrl.isNotBlank()) { + Glide.with(context) + .load(imageUrl) + .transition(DrawableTransitionOptions.withCrossFade()) + .centerCrop() + .placeholder(R.drawable.bg_product_card) + .into(binding.ivCartThumb) + } else { + binding.ivCartThumb.setImageResource(R.drawable.splash) + } + + binding.btnPlus.setOnClickListener { + onQuantityChanged(item, 1) + } + + binding.btnMinus.setOnClickListener { + onQuantityChanged(item, -1) + } + + binding.btnRemoveCartItem.setOnClickListener { + onItemRemoved(item) + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CartViewHolder { + val binding = ItemCartItemBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + return CartViewHolder(binding) + } + + override fun onBindViewHolder(holder: CartViewHolder, position: Int) { + holder.bind(items[position]) + } + + override fun getItemCount(): Int = items.size + + fun updateData(newItems: List) { + items.clear() + items.addAll(newItems) + notifyDataSetChanged() + } +} diff --git a/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/CartBottomSheet.kt b/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/CartBottomSheet.kt new file mode 100644 index 0000000..3326625 --- /dev/null +++ b/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/CartBottomSheet.kt @@ -0,0 +1,152 @@ +package co.dripzone.app.ui + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import co.dripzone.app.databinding.BottomSheetCartBinding +import co.dripzone.app.model.CartItem +import co.dripzone.app.model.CartManager +import co.dripzone.app.model.City +import co.dripzone.app.security.SecurityManager +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import java.text.NumberFormat +import java.util.Locale + +class CartBottomSheet : BottomSheetDialogFragment() { + + private var _binding: BottomSheetCartBinding? = null + private val binding get() = _binding!! + + private var cartAdapter: CartAdapter? = null + private var selectedCity: City = City.defaultCities().first() + private var onOpenCitySelectorListener: (() -> Unit)? = null + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = BottomSheetCartBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.btnCloseCart.setOnClickListener { + dismiss() + } + + binding.cardChangeCity.setOnClickListener { + onOpenCitySelectorListener?.invoke() + } + + val items = CartManager.getItems().toMutableList() + cartAdapter = CartAdapter( + items = items, + onQuantityChanged = { item, delta -> + CartManager.updateQuantity(requireContext(), item.product.id, item.selectedSize, delta) + refreshCartUI() + }, + onItemRemoved = { item -> + CartManager.removeItem(requireContext(), item.product.id, item.selectedSize) + refreshCartUI() + } + ) + binding.rvCartItems.adapter = cartAdapter + + binding.btnCheckoutWhatsApp.setOnClickListener { + val cartList = CartManager.getItems() + if (cartList.isEmpty()) return@setOnClickListener + + val subtotal = CartManager.getSubtotal() + val total = subtotal + selectedCity.shippingCost + val balanceDue = if (total > 10000.0) total - 10000.0 else 0.0 + + val itemsText = cartList.joinToString("\n") { + "👟 *${it.product.name}* (Talla: *${it.selectedSize}*, Cant: ${it.quantity}) - ${it.formattedItemTotal()}" + } + + val format = NumberFormat.getCurrencyInstance(Locale("es", "CO")) + format.maximumFractionDigits = 0 + val formattedTotal = format.format(total) + " COP" + val formattedBalance = format.format(balanceDue) + " COP" + + val message = buildString { + append("¡Hola DripZone! 🔥 Quiero ordenar este pedido desde la app móvil:\n\n") + append("📦 *RESUMEN DEL PEDIDO:*\n") + append(itemsText) + append("\n\n📍 *Ciudad de Despacho:* ${selectedCity.name} (${selectedCity.formattedShipping()})\n") + append("💰 *TOTAL A PAGAR:* $formattedTotal\n") + append("⚡ *Condición:* Pago Contraentrega (Separo con $10.000 COP y cancelo saldo de $formattedBalance al recibir en mi puerta).\n\n") + append("¿Me confirman disponibilidad para programar el despacho?") + } + + SecurityManager.openWhatsApp( + requireContext(), + SecurityManager.WHATSAPP_STORE_PHONE, + message + ) + dismiss() + } + + refreshCartUI() + } + + fun setSelectedCity(city: City) { + selectedCity = city + if (_binding != null) { + refreshCartUI() + } + } + + private fun refreshCartUI() { + val items = CartManager.getItems() + cartAdapter?.updateData(items) + + val count = CartManager.getTotalCount() + binding.tvCartCountSubtitle.text = "$count ${if (count == 1) "artículo seleccionado" else "artículos seleccionados"}" + + if (items.isEmpty()) { + binding.layoutCartEmpty.visibility = View.VISIBLE + binding.layoutCartContent.visibility = View.GONE + } else { + binding.layoutCartEmpty.visibility = View.GONE + binding.layoutCartContent.visibility = View.VISIBLE + + binding.tvCartCityName.text = "Destino: ${selectedCity.name}" + binding.tvCartCityInfo.text = "${selectedCity.deliveryTime} • Toca para cambiar ciudad" + binding.tvCartShippingFee.text = selectedCity.formattedShipping() + + val subtotal = CartManager.getSubtotal() + val shipping = selectedCity.shippingCost + val total = subtotal + shipping + val balanceDue = if (total > 10000.0) total - 10000.0 else 0.0 + + val format = NumberFormat.getCurrencyInstance(Locale("es", "CO")) + format.maximumFractionDigits = 0 + + binding.tvSubtotal.text = format.format(subtotal) + " COP" + binding.tvShippingTotal.text = selectedCity.formattedShipping() + binding.tvCartTotalAmount.text = format.format(total) + " COP" + binding.tvSeparationBreakdown.text = "Separas tu pedido con $10.000 COP y cancelas el saldo restante (${format.format(balanceDue)} COP) en efectivo al recibir." + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + companion object { + const val TAG = "CartBottomSheet" + + fun newInstance(city: City, onOpenCitySelector: () -> Unit): CartBottomSheet { + return CartBottomSheet().apply { + this.selectedCity = city + this.onOpenCitySelectorListener = onOpenCitySelector + } + } + } +} diff --git a/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/CityAdapter.kt b/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/CityAdapter.kt new file mode 100644 index 0000000..6374a2d --- /dev/null +++ b/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/CityAdapter.kt @@ -0,0 +1,61 @@ +package co.dripzone.app.ui + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import co.dripzone.app.R +import co.dripzone.app.databinding.ItemCityBinding +import co.dripzone.app.model.City + +class CityAdapter( + private val cities: List, + private var selectedCityId: String, + private val onCitySelected: (City) -> Unit +) : RecyclerView.Adapter() { + + inner class CityViewHolder(private val binding: ItemCityBinding) : + RecyclerView.ViewHolder(binding.root) { + + fun bind(city: City) { + val isSelected = city.id == selectedCityId + val context = binding.root.context + + binding.tvCityName.text = city.name + binding.tvCityDeliveryTime.text = "${city.deliveryTime} • ${city.department}" + binding.tvShippingCost.text = city.formattedShipping() + + if (isSelected) { + binding.ivCheck.visibility = View.VISIBLE + binding.cardCity.strokeColor = ContextCompat.getColor(context, R.color.drip_fuchsia) + binding.cardCity.setCardBackgroundColor(ContextCompat.getColor(context, R.color.bg_card_elevated)) + } else { + binding.ivCheck.visibility = View.INVISIBLE + binding.cardCity.strokeColor = ContextCompat.getColor(context, R.color.bg_card_stroke) + binding.cardCity.setCardBackgroundColor(ContextCompat.getColor(context, R.color.bg_card)) + } + + binding.cardCity.setOnClickListener { + selectedCityId = city.id + notifyDataSetChanged() + onCitySelected(city) + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CityViewHolder { + val binding = ItemCityBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + return CityViewHolder(binding) + } + + override fun onBindViewHolder(holder: CityViewHolder, position: Int) { + holder.bind(cities[position]) + } + + override fun getItemCount(): Int = cities.size +} diff --git a/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/CitySelectorBottomSheet.kt b/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/CitySelectorBottomSheet.kt new file mode 100644 index 0000000..28d5777 --- /dev/null +++ b/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/CitySelectorBottomSheet.kt @@ -0,0 +1,67 @@ +package co.dripzone.app.ui + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import co.dripzone.app.databinding.BottomSheetCitySelectorBinding +import co.dripzone.app.model.City +import com.google.android.material.bottomsheet.BottomSheetDialogFragment + +class CitySelectorBottomSheet : BottomSheetDialogFragment() { + + private var _binding: BottomSheetCitySelectorBinding? = null + private val binding get() = _binding!! + + private var cities: List = emptyList() + private var selectedCity: City? = null + private var onCitySelectedListener: ((City) -> Unit)? = null + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = BottomSheetCitySelectorBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.btnCloseCity.setOnClickListener { + dismiss() + } + + val adapter = CityAdapter( + cities = cities, + selectedCityId = selectedCity?.id ?: "cali" + ) { city -> + onCitySelectedListener?.invoke(city) + dismiss() + } + + binding.rvCities.adapter = adapter + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + companion object { + const val TAG = "CitySelectorBottomSheet" + + fun newInstance( + cities: List, + selectedCity: City, + onSelected: (City) -> Unit + ): CitySelectorBottomSheet { + return CitySelectorBottomSheet().apply { + this.cities = cities + this.selectedCity = selectedCity + this.onCitySelectedListener = onSelected + } + } + } +} diff --git a/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/ProductDetailBottomSheet.kt b/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/ProductDetailBottomSheet.kt index d8b6f4d..73e74df 100644 --- a/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/ProductDetailBottomSheet.kt +++ b/dripzone-app/android/app/src/main/java/co/dripzone/app/ui/ProductDetailBottomSheet.kt @@ -66,6 +66,18 @@ class ProductDetailBottomSheet : BottomSheetDialogFragment() { dismiss() } + // Add to Cart Button + binding.btnAddToCart.setOnClickListener { + val sizeToOrder = if (selectedSize.isNotBlank()) selectedSize else sizeAdapter?.getSelectedSize() ?: "38-43" + co.dripzone.app.model.CartManager.addItem(requireContext(), p, sizeToOrder, 1) + android.widget.Toast.makeText( + requireContext(), + "¡Agregado al Carrito! (${p.name} - Talla $sizeToOrder)", + android.widget.Toast.LENGTH_SHORT + ).show() + dismiss() + } + // WhatsApp Order Checkout Button binding.btnOrderWhatsApp.setOnClickListener { val sizeToOrder = if (selectedSize.isNotBlank()) selectedSize else sizeAdapter?.getSelectedSize() ?: "Por coordinar" diff --git a/dripzone-app/android/app/src/main/res/drawable-land-hdpi/dripzone_splash_clean.png b/dripzone-app/android/app/src/main/res/drawable-land-hdpi/dripzone_splash_clean.png new file mode 100644 index 0000000..ffa1d55 Binary files /dev/null and b/dripzone-app/android/app/src/main/res/drawable-land-hdpi/dripzone_splash_clean.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-land-hdpi/splash.png b/dripzone-app/android/app/src/main/res/drawable-land-hdpi/splash.png index 3b78ce2..ffa1d55 100644 Binary files a/dripzone-app/android/app/src/main/res/drawable-land-hdpi/splash.png and b/dripzone-app/android/app/src/main/res/drawable-land-hdpi/splash.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-land-mdpi/dripzone_splash_clean.png b/dripzone-app/android/app/src/main/res/drawable-land-mdpi/dripzone_splash_clean.png new file mode 100644 index 0000000..985ddff Binary files /dev/null and b/dripzone-app/android/app/src/main/res/drawable-land-mdpi/dripzone_splash_clean.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-land-mdpi/splash.png b/dripzone-app/android/app/src/main/res/drawable-land-mdpi/splash.png index 878f728..985ddff 100644 Binary files a/dripzone-app/android/app/src/main/res/drawable-land-mdpi/splash.png and b/dripzone-app/android/app/src/main/res/drawable-land-mdpi/splash.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-land-xhdpi/dripzone_splash_clean.png b/dripzone-app/android/app/src/main/res/drawable-land-xhdpi/dripzone_splash_clean.png new file mode 100644 index 0000000..3b8a581 Binary files /dev/null and b/dripzone-app/android/app/src/main/res/drawable-land-xhdpi/dripzone_splash_clean.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-land-xhdpi/splash.png b/dripzone-app/android/app/src/main/res/drawable-land-xhdpi/splash.png index b8f66cf..3b8a581 100644 Binary files a/dripzone-app/android/app/src/main/res/drawable-land-xhdpi/splash.png and b/dripzone-app/android/app/src/main/res/drawable-land-xhdpi/splash.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-land-xxhdpi/dripzone_splash_clean.png b/dripzone-app/android/app/src/main/res/drawable-land-xxhdpi/dripzone_splash_clean.png new file mode 100644 index 0000000..a7d399b Binary files /dev/null and b/dripzone-app/android/app/src/main/res/drawable-land-xxhdpi/dripzone_splash_clean.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-land-xxhdpi/splash.png b/dripzone-app/android/app/src/main/res/drawable-land-xxhdpi/splash.png index edb90e9..a7d399b 100644 Binary files a/dripzone-app/android/app/src/main/res/drawable-land-xxhdpi/splash.png and b/dripzone-app/android/app/src/main/res/drawable-land-xxhdpi/splash.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-land-xxxhdpi/dripzone_splash_clean.png b/dripzone-app/android/app/src/main/res/drawable-land-xxxhdpi/dripzone_splash_clean.png new file mode 100644 index 0000000..f24903a Binary files /dev/null and b/dripzone-app/android/app/src/main/res/drawable-land-xxxhdpi/dripzone_splash_clean.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-land-xxxhdpi/splash.png b/dripzone-app/android/app/src/main/res/drawable-land-xxxhdpi/splash.png index 6848ef6..f24903a 100644 Binary files a/dripzone-app/android/app/src/main/res/drawable-land-xxxhdpi/splash.png and b/dripzone-app/android/app/src/main/res/drawable-land-xxxhdpi/splash.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-port-hdpi/dripzone_splash_clean.png b/dripzone-app/android/app/src/main/res/drawable-port-hdpi/dripzone_splash_clean.png new file mode 100644 index 0000000..93e455f Binary files /dev/null and b/dripzone-app/android/app/src/main/res/drawable-port-hdpi/dripzone_splash_clean.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-port-hdpi/splash.png b/dripzone-app/android/app/src/main/res/drawable-port-hdpi/splash.png index ec658a5..93e455f 100644 Binary files a/dripzone-app/android/app/src/main/res/drawable-port-hdpi/splash.png and b/dripzone-app/android/app/src/main/res/drawable-port-hdpi/splash.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-port-mdpi/dripzone_splash_clean.png b/dripzone-app/android/app/src/main/res/drawable-port-mdpi/dripzone_splash_clean.png new file mode 100644 index 0000000..b5c713e Binary files /dev/null and b/dripzone-app/android/app/src/main/res/drawable-port-mdpi/dripzone_splash_clean.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-port-mdpi/splash.png b/dripzone-app/android/app/src/main/res/drawable-port-mdpi/splash.png index 3cbbce5..b5c713e 100644 Binary files a/dripzone-app/android/app/src/main/res/drawable-port-mdpi/splash.png and b/dripzone-app/android/app/src/main/res/drawable-port-mdpi/splash.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-port-xhdpi/dripzone_splash_clean.png b/dripzone-app/android/app/src/main/res/drawable-port-xhdpi/dripzone_splash_clean.png new file mode 100644 index 0000000..de29117 Binary files /dev/null and b/dripzone-app/android/app/src/main/res/drawable-port-xhdpi/dripzone_splash_clean.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-port-xhdpi/splash.png b/dripzone-app/android/app/src/main/res/drawable-port-xhdpi/splash.png index e2f34aa..de29117 100644 Binary files a/dripzone-app/android/app/src/main/res/drawable-port-xhdpi/splash.png and b/dripzone-app/android/app/src/main/res/drawable-port-xhdpi/splash.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-port-xxhdpi/dripzone_splash_clean.png b/dripzone-app/android/app/src/main/res/drawable-port-xxhdpi/dripzone_splash_clean.png new file mode 100644 index 0000000..1bb2c9f Binary files /dev/null and b/dripzone-app/android/app/src/main/res/drawable-port-xxhdpi/dripzone_splash_clean.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-port-xxhdpi/splash.png b/dripzone-app/android/app/src/main/res/drawable-port-xxhdpi/splash.png index 817dfae..1bb2c9f 100644 Binary files a/dripzone-app/android/app/src/main/res/drawable-port-xxhdpi/splash.png and b/dripzone-app/android/app/src/main/res/drawable-port-xxhdpi/splash.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-port-xxxhdpi/dripzone_splash_clean.png b/dripzone-app/android/app/src/main/res/drawable-port-xxxhdpi/dripzone_splash_clean.png new file mode 100644 index 0000000..0b85dad Binary files /dev/null and b/dripzone-app/android/app/src/main/res/drawable-port-xxxhdpi/dripzone_splash_clean.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable-port-xxxhdpi/splash.png b/dripzone-app/android/app/src/main/res/drawable-port-xxxhdpi/splash.png index 0e408fd..0b85dad 100644 Binary files a/dripzone-app/android/app/src/main/res/drawable-port-xxxhdpi/splash.png and b/dripzone-app/android/app/src/main/res/drawable-port-xxxhdpi/splash.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable/bg_badge_counter.xml b/dripzone-app/android/app/src/main/res/drawable/bg_badge_counter.xml new file mode 100644 index 0000000..7ebe972 --- /dev/null +++ b/dripzone-app/android/app/src/main/res/drawable/bg_badge_counter.xml @@ -0,0 +1,9 @@ + + + + diff --git a/dripzone-app/android/app/src/main/res/drawable/bg_badge_drip.xml b/dripzone-app/android/app/src/main/res/drawable/bg_badge_drip.xml new file mode 100644 index 0000000..73c2651 --- /dev/null +++ b/dripzone-app/android/app/src/main/res/drawable/bg_badge_drip.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/dripzone-app/android/app/src/main/res/drawable/bg_banner_gradient.xml b/dripzone-app/android/app/src/main/res/drawable/bg_banner_gradient.xml new file mode 100644 index 0000000..cfa4220 --- /dev/null +++ b/dripzone-app/android/app/src/main/res/drawable/bg_banner_gradient.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/dripzone-app/android/app/src/main/res/drawable/bg_btn_drip.xml b/dripzone-app/android/app/src/main/res/drawable/bg_btn_drip.xml new file mode 100644 index 0000000..9ef303c --- /dev/null +++ b/dripzone-app/android/app/src/main/res/drawable/bg_btn_drip.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/dripzone-app/android/app/src/main/res/drawable/bg_chip_category.xml b/dripzone-app/android/app/src/main/res/drawable/bg_chip_category.xml index b3ed10f..3fcf711 100644 --- a/dripzone-app/android/app/src/main/res/drawable/bg_chip_category.xml +++ b/dripzone-app/android/app/src/main/res/drawable/bg_chip_category.xml @@ -2,7 +2,11 @@ - + diff --git a/dripzone-app/android/app/src/main/res/drawable/bg_chip_size.xml b/dripzone-app/android/app/src/main/res/drawable/bg_chip_size.xml index 7971f32..51101ed 100644 --- a/dripzone-app/android/app/src/main/res/drawable/bg_chip_size.xml +++ b/dripzone-app/android/app/src/main/res/drawable/bg_chip_size.xml @@ -2,7 +2,11 @@ - + diff --git a/dripzone-app/android/app/src/main/res/drawable/dripzone_splash_clean.png b/dripzone-app/android/app/src/main/res/drawable/dripzone_splash_clean.png new file mode 100644 index 0000000..b693e55 Binary files /dev/null and b/dripzone-app/android/app/src/main/res/drawable/dripzone_splash_clean.png differ diff --git a/dripzone-app/android/app/src/main/res/drawable/ic_home.xml b/dripzone-app/android/app/src/main/res/drawable/ic_home.xml new file mode 100644 index 0000000..4914da3 --- /dev/null +++ b/dripzone-app/android/app/src/main/res/drawable/ic_home.xml @@ -0,0 +1,9 @@ + + + diff --git a/dripzone-app/android/app/src/main/res/drawable/ic_pin.xml b/dripzone-app/android/app/src/main/res/drawable/ic_pin.xml new file mode 100644 index 0000000..a00e5af --- /dev/null +++ b/dripzone-app/android/app/src/main/res/drawable/ic_pin.xml @@ -0,0 +1,9 @@ + + + diff --git a/dripzone-app/android/app/src/main/res/drawable/ic_shopping_bag.xml b/dripzone-app/android/app/src/main/res/drawable/ic_shopping_bag.xml new file mode 100644 index 0000000..34ae3c7 --- /dev/null +++ b/dripzone-app/android/app/src/main/res/drawable/ic_shopping_bag.xml @@ -0,0 +1,9 @@ + + + diff --git a/dripzone-app/android/app/src/main/res/drawable/splash.png b/dripzone-app/android/app/src/main/res/drawable/splash.png index ec658a5..b693e55 100644 Binary files a/dripzone-app/android/app/src/main/res/drawable/splash.png and b/dripzone-app/android/app/src/main/res/drawable/splash.png differ diff --git a/dripzone-app/android/app/src/main/res/layout/activity_main.xml b/dripzone-app/android/app/src/main/res/layout/activity_main.xml index 3cfe194..933a596 100644 --- a/dripzone-app/android/app/src/main/res/layout/activity_main.xml +++ b/dripzone-app/android/app/src/main/res/layout/activity_main.xml @@ -14,39 +14,82 @@ android:background="@color/bg_dark" app:elevation="0dp"> - + + + + + + + + + + android:paddingTop="10dp" + android:paddingBottom="6dp"> - + - + android:orientation="horizontal"> + + + + + @@ -58,52 +101,130 @@ android:layout_height="40dp" android:background="?attr/selectableItemBackgroundBorderless" android:contentDescription="Buscar" - android:src="@drawable/ic_search" /> + android:src="@drawable/ic_search" + app:tint="@color/text_secondary" /> - + + + + + + + + + + app:iconPadding="4dp" + app:iconSize="14dp" /> - + + + + + + + + + + + + android:paddingHorizontal="12dp" + android:visibility="gone"> + android:src="@drawable/ic_search" + app:tint="@color/drip_purple_light" /> - - - - - - - - @@ -205,7 +298,8 @@ + android:src="@drawable/ic_search" + app:tint="@color/drip_fuchsia" /> @@ -243,28 +337,188 @@ android:layout_width="48dp" android:layout_height="48dp" android:layout_gravity="center" - android:indeterminateTint="@color/accent_green" + android:indeterminateTint="@color/drip_fuchsia" android:visibility="gone" /> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dripzone-app/android/app/src/main/res/layout/activity_splash.xml b/dripzone-app/android/app/src/main/res/layout/activity_splash.xml new file mode 100644 index 0000000..690f492 --- /dev/null +++ b/dripzone-app/android/app/src/main/res/layout/activity_splash.xml @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/dripzone-app/android/app/src/main/res/layout/bottom_sheet_cart.xml b/dripzone-app/android/app/src/main/res/layout/bottom_sheet_cart.xml new file mode 100644 index 0000000..5193e3a --- /dev/null +++ b/dripzone-app/android/app/src/main/res/layout/bottom_sheet_cart.xml @@ -0,0 +1,302 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dripzone-app/android/app/src/main/res/layout/bottom_sheet_city_selector.xml b/dripzone-app/android/app/src/main/res/layout/bottom_sheet_city_selector.xml new file mode 100644 index 0000000..eb3a71e --- /dev/null +++ b/dripzone-app/android/app/src/main/res/layout/bottom_sheet_city_selector.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/dripzone-app/android/app/src/main/res/layout/bottom_sheet_product_detail.xml b/dripzone-app/android/app/src/main/res/layout/bottom_sheet_product_detail.xml index 4675970..7099bfc 100644 --- a/dripzone-app/android/app/src/main/res/layout/bottom_sheet_product_detail.xml +++ b/dripzone-app/android/app/src/main/res/layout/bottom_sheet_product_detail.xml @@ -21,7 +21,7 @@ android:layout_marginBottom="16dp" android:background="#40FFFFFF" /> - + @@ -31,12 +31,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" - android:background="@drawable/bg_chip_category" + android:background="@drawable/bg_badge_drip" android:fontFamily="sans-serif-black" android:paddingHorizontal="10dp" android:paddingVertical="4dp" android:text="NIKE AIR" - android:textColor="@color/accent_green" + android:textColor="@color/badge_drip_text" android:textSize="11sp" /> + android:src="@drawable/ic_close" + app:tint="@color/text_secondary" /> @@ -75,11 +76,11 @@ android:layout_height="wrap_content" android:layout_marginTop="16dp" android:fontFamily="sans-serif-black" - android:text="Nike SB Dunk Low Pro 'Chicago'" + android:text="Nike SB Dunk Low Pro" android:textColor="@color/text_primary" android:textSize="18sp" /> - + @@ -103,7 +104,8 @@ + android:src="@drawable/ic_truck" + app:tint="@color/accent_gold" /> @@ -149,14 +151,33 @@ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" tools:listitem="@layout/item_size_chip" /> + + + diff --git a/dripzone-app/android/app/src/main/res/layout/dialog_custom_order.xml b/dripzone-app/android/app/src/main/res/layout/dialog_custom_order.xml index 6066dac..cfa003a 100644 --- a/dripzone-app/android/app/src/main/res/layout/dialog_custom_order.xml +++ b/dripzone-app/android/app/src/main/res/layout/dialog_custom_order.xml @@ -31,7 +31,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="6dp" - android:text="¿Buscas un modelo exclusivo o una talla específica de nuestros proveedores? Nosotros te lo conseguimos." + android:text="¿Buscas un modelo exclusivo o una talla específica de nuestros proveedores? Te lo conseguimos bajo pedido." android:textColor="@color/text_secondary" android:textSize="13sp" /> @@ -48,7 +48,7 @@ app:boxCornerRadiusBottomStart="12dp" app:boxCornerRadiusTopEnd="12dp" app:boxCornerRadiusTopStart="12dp" - app:boxStrokeColor="@color/accent_green"> + app:boxStrokeColor="@color/drip_fuchsia"> + app:boxStrokeColor="@color/drip_fuchsia"> + app:boxStrokeColor="@color/drip_fuchsia"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dripzone-app/android/app/src/main/res/layout/item_city.xml b/dripzone-app/android/app/src/main/res/layout/item_city.xml new file mode 100644 index 0000000..a422d77 --- /dev/null +++ b/dripzone-app/android/app/src/main/res/layout/item_city.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/dripzone-app/android/app/src/main/res/layout/item_product.xml b/dripzone-app/android/app/src/main/res/layout/item_product.xml index b00a1f0..9c6baab 100644 --- a/dripzone-app/android/app/src/main/res/layout/item_product.xml +++ b/dripzone-app/android/app/src/main/res/layout/item_product.xml @@ -32,19 +32,19 @@ android:contentDescription="Foto del producto" android:scaleType="centerCrop" /> - + @@ -55,7 +55,7 @@ android:layout_height="wrap_content" android:layout_gravity="top|end" android:layout_margin="8dp" - android:background="#99000000" + android:background="#B3000000" android:paddingHorizontal="6dp" android:paddingVertical="3dp" android:text="CONTRAENTREGA" @@ -109,7 +109,7 @@ android:layout_weight="1" android:fontFamily="sans-serif-black" android:text="$ 280.000" - android:textColor="@color/accent_green" + android:textColor="@color/drip_purple_glow" android:textSize="14sp" /> diff --git a/dripzone-app/android/app/src/main/res/values/colors.xml b/dripzone-app/android/app/src/main/res/values/colors.xml index c82c3bc..ea9e031 100644 --- a/dripzone-app/android/app/src/main/res/values/colors.xml +++ b/dripzone-app/android/app/src/main/res/values/colors.xml @@ -1,31 +1,43 @@ - #10B981 - #059669 - #10B981 + + #A855F7 + #7E22CE + #D946EF - #0B0B0E - #16161A - #22222A - #2D2D38 - #1A1A22 + + #0A0A0A + #171717 + #212126 + #282830 + #A855F7 + #1A1A20 - #10B981 - #34D399 + + #9333EA + #A855F7 + #D946EF + #EC4899 + #C084FC + + #25D366 #1EBE5D - #F97316 #F59E0B + #10B981 - #FFFFFF - #9CA3AF - #6B7280 - #27272A + + #EDEDED + #A1A1AA + #71717A + #262626 - #2E1065 - #C084FC + + #2E1065 + #581C87 + #D8B4FE #064E3B #34D399 #1E3A8A - #60A5FA + #93C5FD diff --git a/dripzone-app/android/app/src/main/res/values/styles.xml b/dripzone-app/android/app/src/main/res/values/styles.xml index 163ae4c..48176e4 100644 --- a/dripzone-app/android/app/src/main/res/values/styles.xml +++ b/dripzone-app/android/app/src/main/res/values/styles.xml @@ -1,13 +1,13 @@ - + - + - +