Scan to open on your phone
Point a camera at the QR code - it opens this exact paste, no app needed.
https://codepastes.com/zzgs9gwdt87bc2j5sin-tituloPlain Text
package com.negocio.desinfectantes.ui.inventario
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.negocio.desinfectantes.data.*
private val Navy = Color(0xFF101D35)
private val CardBlue = Color(0xFF1B2D4A)
private val AccentBlue = Color(0xFF2585F5)
private val SoftBlue = Color(0xFFA9BEDB)
private val GreenStock = Color(0xFF81C784)
private val OrangeReserved = Color(0xFFFFB74D)
@Composable
fun InventarioScreen(
modifier: Modifier = Modifier,
productos: List<ProductoEntity>,
aromas: List<AromaEntity>,
presentaciones: List<PresentacionEntity>,
inventarios: List<InventarioEntity>,
onVolver: () -> Unit
) {
val totalDisponibleBarrancas = inventarios
.filter { it.sede == "Barrancas" }
.sumOf { it.cantidadDisponible }
val totalReservadoBarrancas = inventarios
.filter { it.sede == "Barrancas" }
.sumOf { it.cantidadReservada }
val totalDisponibleValledupar = inventarios
.filter { it.sede == "Valledupar" }
.sumOf { it.cantidadDisponible }
val totalReservadoValledupar = inventarios
.filter { it.sede == "Valledupar" }
.sumOf { it.cantidadReservada }
val totalDisponible = totalDisponibleBarrancas +
totalDisponibleValledupar
val totalReservado = totalReservadoBarrancas +
totalReservadoValledupar
val totalGeneral = totalDisponible + totalReservado
Column(
modifier = modifier
.fillMaxSize()
.background(Navy)
.verticalScroll(rememberScrollState())
.padding(20.dp)
) {
// ENCABEZADO
TextButton(onClick = onVolver) {
Text("← Volver", color = SoftBlue)
}
Text(
"Inventario",
color = Color.White,
style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(8.dp))
Text(
"Consulta las existencias disponibles y reservadas en cada sede.",
color = SoftBlue,
style = MaterialTheme.typography.bodyMedium
)
Spacer(modifier = Modifier.height(24.dp))
// RESUMEN POR SEDES
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
// BARRANCAS
Card(
modifier = Modifier.weight(1f),
colors = CardDefaults.cardColors(
containerColor = CardBlue
),
shape = RoundedCornerShape(16.dp)
) {
Column(
modifier = Modifier.padding(12.dp)
) {
Text(
"Barrancas",
color = SoftBlue,
style = MaterialTheme.typography.bodySmall
)
Spacer(modifier = Modifier.height(8.dp))
Text(
"$totalDisponibleBarrancas",
color = GreenStock,
style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.Bold
)
Text(
"Disponibles",
color = SoftBlue,
style = MaterialTheme.typography.bodySmall
)
Spacer(modifier = Modifier.height(8.dp))
Text(
"$totalReservadoBarrancas",
color = OrangeReserved,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold
)
Text(
"Reservadas",
color = SoftBlue,
style = MaterialTheme.typography.bodySmall
)
}
}
// VALLEDUPAR
Card(
modifier = Modifier.weight(1f),
colors = CardDefaults.cardColors(
containerColor = CardBlue
),
shape = RoundedCornerShape(16.dp)
) {
Column(
modifier = Modifier.padding(12.dp)
) {
Text(
"Valledupar",
color = SoftBlue,
style = MaterialTheme.typography.bodySmall
)
Spacer(modifier = Modifier.height(8.dp))
Text(
"$totalDisponibleValledupar",
color = GreenStock,
style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.Bold
)
Text(
"Disponibles",
color = SoftBlue,
style = MaterialTheme.typography.bodySmall
)
Spacer(modifier = Modifier.height(8.dp))
Text(
"$totalReservadoValledupar",
color = OrangeReserved,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold
)
Text(
"Reservadas",
color = SoftBlue,
style = MaterialTheme.typography.bodySmall
)
}
}
// TOTAL GENERAL
Card(
modifier = Modifier.weight(1f),
colors = CardDefaults.cardColors(
containerColor = AccentBlue
),
shape = RoundedCornerShape(16.dp)
) {
Column(
modifier = Modifier.padding(12.dp)
) {
Text(
"Total general",
color = Color.White,
style = MaterialTheme.typography.bodySmall
)
Spacer(modifier = Modifier.height(8.dp))
Text(
"$totalGeneral",
color = Color.White,
style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.Bold
)
Text(
"Existencias",
color = Color.White.copy(alpha = 0.85f),
style = MaterialTheme.typography.bodySmall
)
Spacer(modifier = Modifier.height(8.dp))
Text(
"$totalDisponible",
color = Color.White,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold
)
Text(
"Disponibles",
color = Color.White.copy(alpha = 0.85f),
style = MaterialTheme.typography.bodySmall
)
Spacer(modifier = Modifier.height(8.dp))
Text(
"$totalReservado",
color = Color.White,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold
)
Text(
"Reservadas",
color = Color.White.copy(alpha = 0.85f),
style = MaterialTheme.typography.bodySmall
)
}
}
}
Spacer(modifier = Modifier.height(28.dp))
// LISTADO DE PRESENTACIONES
Text(
"Existencias por presentación",
color = Color.White,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(14.dp))
if (presentaciones.isEmpty()) {
Text(
"Todavía no hay presentaciones registradas.",
color = SoftBlue
)
} else {
presentaciones
.filter { it.activo }
.forEach { presentacion ->
val aroma = aromas.firstOrNull {
it.id == presentacion.aromaId
}
val producto = productos.firstOrNull {
it.id == aroma?.productoId
}
val inventarioBarrancas = inventarios.firstOrNull {
it.presentacionId == presentacion.id &&
it.sede == "Barrancas"
}
val inventarioValledupar = inventarios.firstOrNull {
it.presentacionId == presentacion.id &&
it.sede == "Valledupar"
}
val stockBarrancas =
inventarioBarrancas?.cantidadDisponible ?: 0
val reservadoBarrancas =
inventarioBarrancas?.cantidadReservada ?: 0
val stockValledupar =
inventarioValledupar?.cantidadDisponible ?: 0
val reservadoValledupar =
inventarioValledupar?.cantidadReservada ?: 0
val disponibleTotal =
stockBarrancas + stockValledupar
val reservadoTotal =
reservadoBarrancas + reservadoValledupar
val existenciasTotales =
disponibleTotal + reservadoTotal
Card(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 14.dp),
colors = CardDefaults.cardColors(
containerColor = CardBlue
),
shape = RoundedCornerShape(16.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text(
producto?.nombre ?: "Producto",
color = Color.White,
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.titleMedium
)
Spacer(modifier = Modifier.height(4.dp))
Text(
"Aroma: ${aroma?.nombre ?: "Sin aroma"}",
color = SoftBlue
)
Text(
"${presentacion.nombre} • ${presentacion.contenidoMl} ml",
color = SoftBlue
)
Spacer(modifier = Modifier.height(16.dp))
HorizontalDivider(
color = SoftBlue.copy(alpha = 0.25f)
)
Spacer(modifier = Modifier.height(12.dp))
// BARRANCAS
Text(
"Barrancas",
color = Color.White,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(6.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text("Disponibles", color = SoftBlue)
Text(
"$stockBarrancas unidades",
color = GreenStock,
fontWeight = FontWeight.Bold
)
}
Spacer(modifier = Modifier.height(5.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text("Reservadas", color = SoftBlue)
Text(
"$reservadoBarrancas unidades",
color = OrangeReserved,
fontWeight = FontWeight.Bold
)
}
Spacer(modifier = Modifier.height(14.dp))
// VALLEDUPAR
Text(
"Valledupar",
color = Color.White,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(6.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text("Disponibles", color = SoftBlue)
Text(
"$stockValledupar unidades",
color = GreenStock,
fontWeight = FontWeight.Bold
)
}
Spacer(modifier = Modifier.height(5.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text("Reservadas", color = SoftBlue)
Text(
"$reservadoValledupar unidades",
color = OrangeReserved,
fontWeight = FontWeight.Bold
)
}
Spacer(modifier = Modifier.height(12.dp))
HorizontalDivider(
color = SoftBlue.copy(alpha = 0.25f)
)
Spacer(modifier = Modifier.height(12.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
"Total disponible",
color = SoftBlue,
fontWeight = FontWeight.Medium
)
Text(
"$disponibleTotal unidades",
color = GreenStock,
fontWeight = FontWeight.Bold
)
}
Spacer(modifier = Modifier.height(6.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
"Total reservado",
color = SoftBlue,
fontWeight = FontWeight.Medium
)
Text(
"$reservadoTotal unidades",
color = OrangeReserved,
fontWeight = FontWeight.Bold
)
}
Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
"Existencias totales",
color = Color.White,
fontWeight = FontWeight.Bold
)
Text(
"$existenciasTotales unidades",
color = AccentBlue,
fontWeight = FontWeight.Bold
)
}
}
}
}
}
Spacer(modifier = Modifier.height(20.dp))
}
}
package com.negocio.desinfectantes
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.negocio.desinfectantes.ui.theme.DesinfectantesAppTheme
import com.negocio.desinfectantes.ui.productos.ProductosScreen
import com.negocio.desinfectantes.ui.productos.AgregarProductoScreen
import com.negocio.desinfectantes.ui.productos.AromasScreen
import com.negocio.desinfectantes.ui.inactivos.RegistrosInactivosScreen
import androidx.compose.ui.platform.LocalContext
import com.negocio.desinfectantes.data.DatabaseProvider
import com.negocio.desinfectantes.data.ProductoEntity
import com.negocio.desinfectantes.data.AromaEntity
import com.negocio.desinfectantes.data.PresentacionEntity
import kotlinx.coroutines.launch
import java.util.UUID
import androidx.compose.runtime.collectAsState
import com.negocio.desinfectantes.ui.productos.AgregarAromaScreen
import com.negocio.desinfectantes.ui.productos.PresentacionesScreen
import com.negocio.desinfectantes.ui.productos.AgregarPresentacionScreen
import com.negocio.desinfectantes.ui.productos.EditarPresentacionScreen
import com.negocio.desinfectantes.ui.produccion.ProduccionScreen
import com.negocio.desinfectantes.ui.inventario.InventarioScreen
import com.negocio.desinfectantes.data.ProduccionEntity
import com.negocio.desinfectantes.ui.clientes.ClienteScreen
import com.negocio.desinfectantes.data.ClienteEntity
import com.negocio.desinfectantes.data.DistribucionProduccionEntity
import com.negocio.desinfectantes.data.InventarioEntity
import com.negocio.desinfectantes.data.MovimientoInventarioEntity
import com.negocio.desinfectantes.ui.ventas.VentaScreen
import com.negocio.desinfectantes.ui.ventas.HistorialVentasScreen
import com.negocio.desinfectantes.ui.reportes.ReportesScreen
import java.util.Calendar
import java.util.Locale
import java.text.NumberFormat
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.res.painterResource
import androidx.compose.foundation.Image
import androidx.compose.ui.graphics.ColorFilter
private val Navy = Color(0xFF101D35)
private val CardBlue = Color(0xFF1B2D4A)
private val AccentBlue = Color(0xFF2585F5)
private val SoftBlue = Color(0xFFA9BEDB)
private val White = Color(0xFFFFFFFF)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
DesinfectantesAppTheme {
PrestigeCleanApp()
}
}
}
}
@Composable
fun PrestigeCleanApp() {
var selectedTab by remember { mutableStateOf("Inicio") }
var ventaInicialId by remember {
mutableStateOf<String?>(null)
}
var mostrarFormularioProducto by remember { mutableStateOf(false) }
var mostrarRegistrosInactivos by remember {
mutableStateOf(false)
}
var productoSeleccionado by remember {
mutableStateOf<ProductoEntity?>(null)
}
var mostrarFormularioAroma by remember {
mutableStateOf(false)
}
var aromaSeleccionado by remember {
mutableStateOf<AromaEntity?>(null)
}
var mostrarFormularioPresentacion by remember {
mutableStateOf(false)
}
var mensajeAroma by remember {
mutableStateOf<String?>(null)
}
var presentacionSeleccionada by remember {
mutableStateOf<PresentacionEntity?>(null)
}
val context = LocalContext.current
val scope = rememberCoroutineScope()
val productoDao = remember {
DatabaseProvider.obtenerBaseDatos(context).productoDao()
}
val productos by productoDao.obtenerProductos()
.collectAsState(initial = emptyList())
val productosInactivos by productoDao.obtenerProductosInactivos()
.collectAsState(initial = emptyList())
val aromasInactivos by productoDao.obtenerTodosAromasInactivos()
.collectAsState(initial = emptyList())
val presentacionesInactivas by productoDao.obtenerTodasPresentacionesInactivas()
.collectAsState(initial = emptyList())
val todosProductos by productoDao.obtenerTodosProductos()
.collectAsState(initial = emptyList())
val todosAromas by productoDao.obtenerTodosAromas()
.collectAsState(initial = emptyList())
val aromasFlow = remember(productoSeleccionado?.id) {
productoSeleccionado?.let {
productoDao.obtenerAromas(it.id)
} ?: kotlinx.coroutines.flow.flowOf(emptyList())
}
val aromas by aromasFlow.collectAsState(initial = emptyList())
val presentacionesFlow = remember(aromaSeleccionado?.id) {
aromaSeleccionado?.let {
productoDao.obtenerPresentaciones(it.id)
} ?: kotlinx.coroutines.flow.flowOf(emptyList())
}
val presentaciones by presentacionesFlow.collectAsState(initial = emptyList())
val todasPresentacionesActivas by productoDao
.obtenerTodasPresentacionesActivas()
.collectAsState(initial = emptyList())
val inventarios by productoDao
.obtenerTodoInventario()
.collectAsState(initial = emptyList())
val clientes by productoDao.obtenerClientes()
.collectAsState(initial = emptyList())
val ventas by productoDao.obtenerVentas()
.collectAsState(initial = emptyList())
val todosLosPagos by productoDao.obtenerTodosLosPagos()
.collectAsState(initial = emptyList())
// FECHAS DEL DIA ACTUAL
var inicioDia by remember {
mutableStateOf(
Calendar.getInstance().apply {
set(Calendar.HOUR_OF_DAY, 0)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}.timeInMillis
)
}
val finDia = remember(inicioDia) {
Calendar.getInstance().apply {
timeInMillis = inicioDia
add(Calendar.DAY_OF_MONTH, 1)
}.timeInMillis
}
// ACTUALIZAR AUTOMATICAMENTE AL LLEGAR LA MEDIANOCHE
LaunchedEffect(Unit) {
while (true) {
val ahora = Calendar.getInstance()
val proximaMedianoche = (ahora.clone() as Calendar).apply {
set(Calendar.HOUR_OF_DAY, 0)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
add(Calendar.DAY_OF_MONTH, 1)
}.timeInMillis
val tiempoEspera = proximaMedianoche - System.currentTimeMillis()
kotlinx.coroutines.delay(tiempoEspera.coerceAtLeast(1L))
inicioDia = Calendar.getInstance().apply {
set(Calendar.HOUR_OF_DAY, 0)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}.timeInMillis
}
}
// ESTADISTICAS DE ROOM
val ventasDelDia by productoDao
.obtenerVentasDelDia(inicioDia, finDia)
.collectAsState(initial = 0.0)
val dineroRecibidoHoy by productoDao
.obtenerDineroRecibidoDelDia(inicioDia, finDia)
.collectAsState(initial = 0.0)
val totalPorCobrar by productoDao
.obtenerTotalPorCobrar()
.collectAsState(initial = 0.0)
val inventarioDisponible by productoDao
.obtenerInventarioDisponible()
.collectAsState(initial = 0)
// FORMATO DE PESOS COLOMBIANOS
val formatoPesos = remember {
NumberFormat.getCurrencyInstance(Locale("es", "CO")).apply {
maximumFractionDigits = 0
minimumFractionDigits = 0
}
}
Scaffold(
containerColor = Navy,
bottomBar = {
NavigationBar(
containerColor = CardBlue,
contentColor = White
) {
NavigationBarItem(
selected = selectedTab == "Inicio",
onClick = { selectedTab = "Inicio" },
icon = { Text("⌂", fontSize = 22.sp) },
label = { Text("Inicio") },
colors = navColors()
)
NavigationBarItem(
selected = selectedTab == "Ventas",
onClick = { selectedTab = "Ventas" },
icon = { Text("$", fontSize = 22.sp) },
label = { Text("Ventas") },
colors = navColors()
)
NavigationBarItem(
selected = selectedTab == "Productos",
onClick = { selectedTab = "Productos" },
icon = {
Icon(
painter = painterResource(id = R.drawable.ic_bottle),
contentDescription = "Productos",
tint = navColors().unselectedIconColor
)
},
label = { Text("Productos") },
colors = navColors()
)
NavigationBarItem(
selected = selectedTab == "Clientes",
onClick = { selectedTab = "Clientes" },
icon = { Text("♙", fontSize = 22.sp) },
label = { Text("Clientes") },
colors = navColors()
)
NavigationBarItem(
selected = selectedTab == "Reportes",
onClick = { selectedTab = "Reportes" },
icon = { Text("▤", fontSize = 22.sp) },
label = { Text("Reportes") },
colors = navColors()
)
}
}
) { innerPadding ->
when (selectedTab) {
"Inicio" -> DashboardScreen(
modifier = Modifier.padding(innerPadding),
onNavigate = { selectedTab = it },
ventasHoy = formatoPesos.format(ventasDelDia),
dineroRecibido = formatoPesos.format(dineroRecibidoHoy),
porCobrar = formatoPesos.format(totalPorCobrar),
inventario = inventarioDisponible.toString()
)
"Ventas" -> VentaScreen(
modifier = Modifier.padding(innerPadding),
clientes = clientes,
productos = productos,
aromas = todosAromas.filter { it.activo },
presentaciones = todasPresentacionesActivas,
productoDao = productoDao,
onVolver = { selectedTab = "Inicio" }
)
"Produccion" -> ProduccionScreen(
modifier = Modifier.padding(innerPadding),
productos = productos,
aromas = todosAromas.filter { it.activo },
presentaciones = todasPresentacionesActivas,
onVolver = {
selectedTab = "Inicio"
},
onGuardar = { lote, detalles, gastos, inventariosLote ->
scope.launch {
try {
productoDao.registrarLoteCompleto(
lote = lote,
detalles = detalles,
gastos = gastos,
inventariosLote = inventariosLote
)
selectedTab = "Inicio"
} catch (e: Exception) {
mensajeAroma = e.message ?: "No se pudo guardar el lote."
}
}
}
)
"Clientes" -> ClienteScreen(
modifier = Modifier.padding(innerPadding),
clientes = clientes,
onGuardar = { cliente ->
scope.launch {
productoDao.guardarCliente(cliente)
}
},
onDesactivar = { clienteId ->
scope.launch {
productoDao.desactivarCliente(clienteId)
}
},
onVolver = {
selectedTab = "Inicio"
}
)
"Historial" -> HistorialVentasScreen(
modifier = Modifier.padding(innerPadding),
ventas = ventas,
clientes = clientes,
productos = todosProductos,
aromas = todosAromas,
presentaciones = todasPresentacionesActivas + presentacionesInactivas,
productoDao = productoDao,
onVolver = {
ventaInicialId = null
selectedTab = "Inicio"
},
ventaInicialId = ventaInicialId
)
"Reportes" -> ReportesScreen(
modifier = Modifier.padding(innerPadding),
ventas = ventas,
clientes = clientes,
pagos = todosLosPagos,
onAbrirVenta = { ventaId ->
ventaInicialId = ventaId
selectedTab = "Historial"
}
)
}
}
if (mensajeAroma != null) {
AlertDialog(
onDismissRequest = {
mensajeAroma = null
},
title = {
Text("No se puede eliminar")
},
text = {
Text(mensajeAroma!!)
},
confirmButton = {
TextButton(
onClick = {
mensajeAroma = null
}
) {
Text("Entendido")
}
}
)
}
}
@Composable
fun navColors() = NavigationBarItemDefaults.colors(
selectedIconColor = AccentBlue,
selectedTextColor = AccentBlue,
indicatorColor = Color(0xFF263F62),
unselectedIconColor = SoftBlue,
unselectedTextColor = SoftBlue
)
@Composable
fun DashboardScreen(
modifier: Modifier = Modifier,
onNavigate: (String) -> Unit,
ventasHoy: String,
dineroRecibido: String,
porCobrar: String,
inventario: String
) {
Column(
modifier = modifier
.fillMaxSize()
.background(Navy)
.verticalScroll(rememberScrollState())
.padding(20.dp),
verticalArrangement = Arrangement.spacedBy(20.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
Box(
modifier = Modifier
.size(48.dp)
.background(AccentBlue, RoundedCornerShape(14.dp)),
contentAlignment = Alignment.Center
) {
Text(
"PC",
color = White,
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
}
Column(modifier = Modifier.weight(1f)) {
Text(
"PRESTIGE CLEAN",
color = White,
fontSize = 17.sp,
fontWeight = FontWeight.Bold
)
Text(
"Panel de administración",
color = SoftBlue,
fontSize = 12.sp
)
}
Text("●", color = Color(0xFF4ADE80), fontSize = 13.sp)
}
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text(
"¡Bienvenido!",
color = White,
fontSize = 25.sp,
fontWeight = FontWeight.Bold
)
Text(
"Este es el resumen de tu negocio.",
color = SoftBlue,
fontSize = 14.sp
)
}
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text(
"Resumen del día",
color = White,
fontSize = 18.sp,
fontWeight = FontWeight.Bold
)
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
MetricCard(
"Ventas de hoy",
ventasHoy,
"Operaciones registradas",
Modifier.weight(1f)
)
MetricCard(
"Dinero recibido",
dineroRecibido,
"Pagos recibidos",
Modifier.weight(1f)
)
}
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
MetricCard(
"Por cobrar",
porCobrar,
"Saldos pendientes",
Modifier.weight(1f)
)
MetricCard(
"Inventario",
inventario,
"Unidades disponibles",
Modifier.weight(1f)
)
}
}
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text(
"Accesos rápidos",
color = White,
fontSize = 18.sp,
fontWeight = FontWeight.Bold
)
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
ActionCard(
"+",
"Nueva venta",
Modifier.weight(1f)
) { onNavigate("Ventas") }
ActionCard(
symbol = "",
title = "Productos",
modifier = Modifier.weight(1f),
iconRes = R.drawable.ic_bottle
) {
onNavigate("Productos")
}
}
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
ActionCard(
"♙",
"Clientes",
Modifier.weight(1f)
) { onNavigate("Clientes") }
ActionCard(
"◷",
"Historial",
Modifier.weight(1f)
) { onNavigate("Historial") }
}
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
ActionCard(
"⚙",
"Producción",
Modifier.weight(1f)
) { onNavigate("Produccion") }
ActionCard(
"▤",
"Inventario",
Modifier.weight(1f)
) { onNavigate("Inventario") }
}
}
Text(
"Prestige Clean • Gestión inteligente de tu negocio",
color = SoftBlue,
fontSize = 11.sp,
modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp),
)
}
}
@Composable
fun MetricCard(
title: String,
value: String,
description: String,
modifier: Modifier = Modifier
) {
Card(
modifier = modifier,
shape = RoundedCornerShape(18.dp),
colors = CardDefaults.cardColors(
containerColor = CardBlue
)
) {
Column(
modifier = Modifier.padding(14.dp),
verticalArrangement = Arrangement.spacedBy(7.dp)
) {
Text(
title,
color = SoftBlue,
fontSize = 12.sp
)
Text(
value,
color = White,
fontSize = 23.sp,
fontWeight = FontWeight.Bold
)
Text(
description,
color = SoftBlue,
fontSize = 10.sp
)
}
}
}
@Composable
fun ActionCard(
symbol: String,
title: String,
modifier: Modifier = Modifier,
iconRes: Int? = null,
onClick: () -> Unit
) {
Card(
modifier = modifier.clickable { onClick() },
shape = RoundedCornerShape(18.dp),
colors = CardDefaults.cardColors(
containerColor = CardBlue
)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
if (iconRes != null) {
Image(
painter = painterResource(id = iconRes),
contentDescription = title,
modifier = Modifier.size(27.dp),
colorFilter = ColorFilter.tint(AccentBlue)
)
} else {
Text(
symbol,
color = AccentBlue,
fontSize = 27.sp,
fontWeight = FontWeight.Bold
)
}
Text(
title,
color = White,
fontSize = 14.sp,
fontWeight = FontWeight.SemiBold
)
}
}
}
@Composable
fun SectionScreen(
title: String,
description: String,
symbol: String,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier
.fillMaxSize()
.background(Navy)
.padding(24.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
title,
color = White,
fontSize = 26.sp,
fontWeight = FontWeight.Bold
)
Box(
modifier = Modifier
.size(70.dp)
.background(CardBlue, RoundedCornerShape(20.dp)),
contentAlignment = Alignment.Center
) {
Text(
symbol,
color = AccentBlue,
fontSize = 32.sp
)
}
Text(
description,
color = SoftBlue,
fontSize = 15.sp
)
Text(
"Estamos preparando esta sección.",
color = White,
fontSize = 13.sp
)
}
}
41,184 chars · codepastes.com