Scan to open on your phone
Point a camera at the QR code - it opens this exact paste, no app needed.
https://codepastes.com/zzgscb1rt81n855bsin-tituloPlain Text
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
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 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())
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 = { Text("▣", fontSize = 22.sp) },
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 }
)
"Ventas" -> SectionScreen(
"Ventas",
"Aquí podremos registrar ventas de contado y a crédito.",
"+",
Modifier.padding(innerPadding)
)
"Productos" -> {
if (mostrarRegistrosInactivos) {
RegistrosInactivosScreen(
productos = productosInactivos,
aromas = aromasInactivos,
presentaciones = presentacionesInactivas,
todosProductos = todosProductos,
todosAromas = todosAromas,
onVolver = {
mostrarRegistrosInactivos = false
},
onReactivarProducto = { producto ->
scope.launch {
productoDao.reactivarProducto(producto.id)
}
},
onReactivarAroma = { aroma ->
scope.launch {
productoDao.reactivarAroma(aroma.id)
}
},
onReactivarPresentacion = { presentacion ->
scope.launch {
productoDao.reactivarPresentacion(presentacion.id)
}
}
)
} else if (mostrarFormularioProducto) {
AgregarProductoScreen(
onVolver = {
mostrarFormularioProducto = false
},
onGuardar = { nombre, tipo, aroma ->
scope.launch {
val productoId = UUID.randomUUID().toString()
val producto = ProductoEntity(
id = productoId,
nombre = nombre,
tipo = tipo
)
val nuevoAroma = AromaEntity(
id = UUID.randomUUID().toString(),
productoId = productoId,
nombre = aroma
)
productoDao.guardarProducto(producto)
productoDao.guardarAroma(nuevoAroma)
mostrarFormularioProducto = false
}
}
)
} else if (productoSeleccionado != null) {
if (mostrarFormularioAroma) {
AgregarAromaScreen(
nombreProducto = productoSeleccionado!!.nombre,
onVolver = {
mostrarFormularioAroma = false
},
onGuardar = { nombreAroma ->
scope.launch {
val nuevoAroma = AromaEntity(
id = UUID.randomUUID().toString(),
productoId = productoSeleccionado!!.id,
nombre = nombreAroma
)
productoDao.guardarAroma(nuevoAroma)
mostrarFormularioAroma = false
}
}
)
} else if (aromaSeleccionado != null) {
if (presentacionSeleccionada != null) {
EditarPresentacionScreen(
nombreProducto = productoSeleccionado!!.nombre,
nombreAroma = aromaSeleccionado!!.nombre,
presentacion = presentacionSeleccionada!!,
onVolver = {
presentacionSeleccionada = null
},
onGuardar = { nombre, contenido, precio ->
scope.launch {
val presentacionActualizada = presentacionSeleccionada!!.copy(
nombre = nombre,
contenidoMl = contenido,
precio = precio
)
productoDao.guardarPresentacion(presentacionActualizada)
presentacionSeleccionada = null
}
},
onEliminar = {
scope.launch {
val idPresentacion = presentacionSeleccionada!!.id
val existencias =
productoDao.contarExistenciasPresentacion(idPresentacion)
if (existencias > 0) {
mensajeAroma =
"No se puede eliminar esta presentación porque todavía tiene $existencias unidades en inventario."
} else {
productoDao.eliminarPresentacion(idPresentacion)
}
presentacionSeleccionada = null
}
}
)
} else if (mostrarFormularioPresentacion) {
AgregarPresentacionScreen(
nombreProducto = productoSeleccionado!!.nombre,
nombreAroma = aromaSeleccionado!!.nombre,
onVolver = {
mostrarFormularioPresentacion = false
},
onGuardar = { nombre, contenido, precio ->
scope.launch {
val nuevaPresentacion = PresentacionEntity(
id = UUID.randomUUID().toString(),
aromaId = aromaSeleccionado!!.id,
nombre = nombre,
contenidoMl = contenido,
precio = precio
)
productoDao.guardarPresentacion(nuevaPresentacion)
mostrarFormularioPresentacion = false
}
}
)
} else {
PresentacionesScreen(
nombreProducto = productoSeleccionado!!.nombre,
nombreAroma = aromaSeleccionado!!.nombre,
presentaciones = presentaciones,
onVolver = {
aromaSeleccionado = null
},
onAgregarPresentacion = {
mostrarFormularioPresentacion = true
},
onSeleccionarPresentacion = { presentacion ->
presentacionSeleccionada = presentacion
}
)
}
} else {
AromasScreen(
nombreProducto = productoSeleccionado!!.nombre,
aromas = aromas,
onVolver = {
productoSeleccionado = null
},
onAgregarAroma = {
mostrarFormularioAroma = true
},
onSeleccionarAroma = { aroma ->
aromaSeleccionado = aroma
},
onEliminarAroma = { aroma ->
scope.launch {
val cantidad = productoDao.contarPresentacionesDeAroma(aroma.id)
if (cantidad == 0) {
productoDao.eliminarAroma(aroma.id)
} else {
mensajeAroma =
"No se puede eliminar este aroma porque tiene $cantidad presentación(es) registradas."
}
}
}
)
}
} else {
ProductosScreen(
modifier = Modifier.padding(innerPadding),
productos = productos,
onAgregarProducto = {
mostrarFormularioProducto = true
},
onVerInactivos = {
mostrarRegistrosInactivos = true
},
onSeleccionarProducto = { producto ->
productoSeleccionado = producto
},
onDesactivarProducto = { producto ->
scope.launch {
val cantidadAromas =
productoDao.contarAromasDeProducto(producto.id)
if (cantidadAromas > 0) {
mensajeAroma =
"No se puede desactivar este producto porque tiene $cantidadAromas aroma(s) registrados."
} else {
productoDao.desactivarProducto(producto.id)
}
}
}
)
}
}
"Clientes" -> SectionScreen(
"Clientes",
"Aquí guardaremos los datos de tus clientes.",
"♙",
Modifier.padding(innerPadding)
)
"Reportes" -> SectionScreen(
"Reportes",
"Aquí consultaremos las ventas, ganancias y movimientos.",
"▤",
Modifier.padding(innerPadding)
)
}
}
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
) {
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",
"$0",
"Operaciones registradas",
Modifier.weight(1f)
)
MetricCard(
"Dinero recibido",
"$0",
"Pagos recibidos",
Modifier.weight(1f)
)
}
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
MetricCard(
"Por cobrar",
"$0",
"Saldos pendientes",
Modifier.weight(1f)
)
MetricCard(
"Inventario",
"0",
"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(
"▣",
"Productos",
Modifier.weight(1f)
) { onNavigate("Productos") }
}
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
ActionCard(
"♙",
"Clientes",
Modifier.weight(1f)
) { onNavigate("Clientes") }
ActionCard(
"◷",
"Historial",
Modifier.weight(1f)
) { onNavigate("Reportes") }
}
}
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,
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)
) {
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
)
}
}
25,817 chars · codepastes.com