cuteofertas
MULTIMARCA · MODA · OFERTAS
Boutique Conny · Sistema de Control
La tienda que se llevaba en un cuaderno, ahora con luz encendida
Sistema de control de tienda: inventario vivo, ventas, fiados con semáforo y cierre de caja que se va por WhatsApp
Desarrollo completo (producto, frontend React, modelo de datos y reglas de Firestore, aprovisionamiento del proyecto Firebase)
Estado
En producción interna — desplegable en Firebase Hosting; el único paso manual pendiente es activar el proveedor Email/Contraseña en la consola de Firebase
Herramienta interna de una tienda real: no hay enlace público ni repositorio abierto.
Del cuaderno a la vitrina
Nadie sabía cuántas unidades quedaban, ni quién debía qué. Todo estaba escrito a mano o recordado de memoria.
Antes · el cuaderno
Lo que nadie podía contestar
Una boutique de ropa multimarca operaba con cuaderno y memoria: nadie sabía con certeza cuántas unidades quedaban de cada categoría, los fiados a clientas y familiares se olvidaban hasta que alguien preguntaba, el cierre del día se cuadraba de cabeza y el contador recibía las ventas del mes en mensajes sueltos. Además la vendedora no debía ver márgenes ni información financiera, pero sí necesitaba operar el día completo sin depender de la dueña.
Después · la app
Una sola app, dos maneras de verla
Una sola app web instalable desde el navegador del teléfono, con navegación por pestañas distinta según el rol. El inventario se maneja por grupo y categoría de producto con stepper de +/- para ajustar existencias en segundos, y viene con un catálogo base de 54 productos precargables desde la lista de precios real de la tienda. Cada venta y cada fiado descuentan del stock dentro de una transacción de Firestore, y guardan un snapshot del producto (grupo, nombre, precio) para que el histórico no cambie aunque después se edite el catálogo. Los fiados llevan un semáforo por fecha promesa (rojo vencido, ámbar por vencer, verde al día, gris liquidado) que reordena la lista sola y hace latir la alerta roja. Al final del día, el cierre calcula totales por método de pago, arma el PDF con la tabla de prendas vendidas y abre WhatsApp con el texto listo para la dueña; el módulo SENIAT hace lo mismo por rango de fechas o mes completo, con exportación a PDF y CSV/Excel.
Siete piezas colgadas del mismo riel
Inventario, ventas atómicas, fiados, cierre, contabilidad, offline y roles. Cada una cuelga del mismo dato en tiempo real.
Inventario por categorías con stepper
Cada producto vive como grupo + nombre + precio + cantidad. Un stepper de − / + ajusta existencias sin abrir formularios, con badge de estado (Agotado, Quedan N, N en stock) y catálogo base de 54 ítems cargable de una vez.
Fiados con semáforo de vencimiento
Cada fiado guarda monto total, abono inicial e historial de abonos, y se pinta según su fecha promesa: rojo vencido (con pulso animado), ámbar a tres días o menos, verde al día, gris liquidado. La lista se ordena por urgencia automáticamente.
Cierre de caja a PDF + WhatsApp
Un botón calcula los totales del día por método de pago, descarga un PDF con encabezado de marca y tabla de prendas, y abre WhatsApp con el informe ya escrito hacia el número de la dueña.
Módulo contable SENIAT
Reporte por rango de fechas o mes completo, con detalle de cada venta (ID, fecha, hora, grupo, producto, cantidad, monto, método) exportable a PDF o a CSV con BOM para que Excel abra bien los acentos.
Funciona sin internet
Firestore se inicializa con caché persistente y gestor multipestaña, y la sesión se guarda en el dispositivo. La tienda sigue vendiendo con la señal caída y todo se sincroniza al volver.
Dos roles, una base de datos
El rol vive en users/{uid} y define tanto las pestañas visibles como las reglas de Firestore: solo la administradora puede anular ventas o fiados y escribir perfiles.
Contabilidad anclada a Caracas
Todas las fechas contables se formatean con Intl en la zona America/Caracas, para que una venta cerca de medianoche no caiga en otro mes fiscal según el reloj del teléfono.
Ocho vistas, todas del mismo dato
La app se abre desde el navegador del teléfono y se instala en la pantalla de inicio. Estas son sus tres primeras pantallas, recreadas aquí en HTML y CSS.
cuteofertas
MULTIMARCA · MODA · OFERTAS
Boutique Conny · Sistema de Control
Correo
Contraseña
Cada quien usa su propio correo y contraseña.
Login con selector de rol
Quién entra decide qué se ve
Inventario
18 productos
Productos
18
Unidades
96
Valor en tienda
$1,842
Vestidos Playeros Largos
12 en stockDamas · PROD-0007
$23.00
Pantalones Jeans Dama
Quedan 3Damas · PROD-0012
$28.00
Gorras Estampadas
AgotadoUnisex · PROD-0031
$10.00
Blusas Manga Larga
7 en stockDamas · PROD-0004
$23.00
Inventario (vendedora)
Stepper de − / + sobre stock real
Venta rápida
18 con stock
Producto vendido *
Vestidos Playeros Largos · Damas — $23.00 (12 disp.)
Blusas Manga Larga · Damas — $23.00 (7 disp.)
Pantalones Jeans Dama · Damas — $28.00 (3 disp.)
Licras Deportivas · Damas — $18.00 (9 disp.)
Cantidad *
Monto cobrado ($) *
Existencia disponible: 12
Al registrar, se descuenta la cantidad vendida del stock automáticamente.
Ventas registradas
2× Blusas Manga Larga
Damas · 2026-06-15 14:32
$46.00
Efectivo1× Vestidos Playeros
Damas · 2026-06-15 12:07
$23.00
Pago MóvilVenta rápida
SearchSelect propio sobre 54 productos
Los fiados se ordenan solos
Cada fiado se pinta por su fecha promesa y la lista se reordena por urgencia sin que nadie toque un filtro. Eso es semaforoSort.
Yulimar
👖 Pantalones Jeans Dama · Damas
🟢 Al día$18.00
de $36.00
📅 30 jun 2026
Abono inicial $18.00
Tía Rosa
👗 Vestidos Casuales Largos · Damas
🔴 ALERTA ROJA · VENCIDO$45.00
de $75.00
📅 22 jun 2026
Abono inicial $30.00
Marielys
🧢 Gorras Estampadas · Unisex
🟡 Por vencer$12.00
de $20.00
📅 18 jun 2026
Abono inicial $8.00
Abono · 12 jun $0.00
Sra. Belkis
👕 Blusas Manga Larga · Damas
✓ Liquidado$0.00
de $46.00
📅 05 jun 2026
Abono inicial $20.00
Abono · 03 jun $26.00
Fiados con semáforo · el bottom-sheet de abono trae los atajos «Mitad» y «Saldar todo», y valida contra el saldo leído del servidor dentro de la transacción.
Lo que sólo ve la dueña
El rol vive en users/{uid} y decide qué pestañas existen. La vendedora nunca llega a esta pantalla, y las reglas de Firestore lo respaldan.
Resumen
Junio 2026
Ingresos del mes
$2,480.00
Vendido hoy
$145.00
Unidades en stock
96
Valor inventario
$1,842.00
Ingresos por método
37 ventas este mes
Ventas por día
Junio 2026 · tooltip «Día 15 · $145.00»
Cuentas por cobrar
Dinero en la calle
Deuda total pendiente
$312.00
🔴 Vencidos (alerta)
2
Resumen / Dashboard (administradora)
Recharts sobre los mismos onSnapshot que alimentan el inventario
Las maquetas reproducen la interfaz real; los datos que muestran son de ejemplo.
El informe sale hacia el teléfono
Un botón cuadra el día por método de pago, arma el PDF con jsPDF y abre WhatsApp con el mensaje ya redactado. Así es como la información circula de verdad en el negocio.
Informe para la dueña
Se envía a Conny · +1 (407) 770-7272
📄 Reporte por período · SENIAT
37 ventas en el período seleccionado · $2.480,00
El CSV lleva BOM UTF-8 para que Excel abra bien los acentos, y el cierre suma sólo ventas: los fiados viven en su propia cuenta.
BOUTIQUE CONNY
Cierre de caja diario
15/06/2026
Total facturado
$145.00
Efectivo
$80.00
Pago Móvil
$45.00
Zelle
$20.00
Prendas vendidas
Generado con jsPDF · autoTable · página 1 de 1
Cierre de caja + informe a la dueña · jsPDF + jspdf-autotable, franja de marca y tabla de prendas
El tamaño real de la tienda
8
pantallas reales (login + 7 vistas)
2
roles con navegación propia
5
colecciones de Firestore
7
operaciones atómicas con runTransaction
3
listeners en tiempo real
54
productos en el catálogo base
6
grupos de inventario
3
métodos de pago soportados
0
Cloud Functions (todo cliente + reglas)
24 cosas que ya hace
Una etiqueta por función, como las que cuelgan de cada prenda de la tienda.
Login con selector visual de rol (Conny administradora / Marisabel vendedora) que precarga el correo correspondiente
Sesión persistente en el dispositivo: no hay que escribir la clave en cada visita
Inventario buscable por producto o grupo, con filtro por los 6 grupos del negocio
Alta de producto con autocompletado de precio desde el catálogo de la lista de precios
Edición de producto en modal (grupo, nombre, precio, cantidad) y borrado con confirmación
Carga masiva del catálogo base de 54 productos con cantidad cero, en un batch de Firestore
Venta rápida con combobox buscador tipo select2, cantidad, monto autocalculado y método de pago
Descuento atómico de stock en la venta: si no hay existencia suficiente, la transacción falla y no se registra nada
Historial de ventas filtrable por rango de fechas y grupo, con total del filtro
Anulación de venta por la administradora, que devuelve las unidades al stock
Fiados: cliente, producto entregado, cantidad, total calculado, abono inicial y fecha promesa obligatoria no pasada
Registro de abonos con atajos de Mitad y Saldar todo, y validación contra el saldo leído del servidor
Marcado automático como Liquidado cuando el saldo llega a cero
Detalle de fiado con historial de pagos y anulación (solo administradora), que devuelve el producto al stock
Dashboard con KPIs animados (ingresos del mes, vendido hoy, unidades en stock, valor de inventario)
Gráfico de dona de ingresos por método de pago y gráfico de barras de ventas por día del mes
Tarjeta de cuentas por cobrar con deuda total y contador de fiados vencidos, con salto directo a la pestaña de Fiados
Cierre de caja por fecha con totales de Efectivo, Pago Móvil y Zelle
Vista previa del mensaje de WhatsApp en un bloque estilo chat, con botón de copiar al portapapeles
Reporte por período con filtro multiselección de grupos, exportado a PDF paginado
Exportación CSV/Excel del período con BOM UTF-8 y fila de total
Códigos secuenciales seguros PROD-0001 / V-0001 / F-0001 generados por transacción sobre meta/counters
Toasts, modales tipo bottom-sheet y estados vacíos ilustrados en toda la app
Respeto de prefers-reduced-motion: todas las animaciones se anulan para quien lo pide
Con qué está cosida
Plan Spark, sin Cloud Functions: todo el peso lo llevan el cliente y las reglas de seguridad.
Frontend
Datos y backend
Gráficos y documentos
Tipografía y marca
Tooling y aprovisionamiento
Arquitectura y lo que costó
SPA de React 19 montada por Vite, sin router: App decide entre Login y AppShell según el estado de autenticación, y AppShell conmuta pantallas por estado local con dos juegos de pestañas (cinco para vendedora, cinco para administradora). Dos contextos sostienen todo: AuthContext escucha onAuthStateChanged, lee el perfil de users/{uid} y expone isAdmin; DataContext abre tres listeners onSnapshot (inventory, sales, credits) ordenados por createdAt y expone las acciones de dominio. Toda escritura que toque stock pasa por runTransaction: las ventas, los fiados, los abonos, las anulaciones y el ajuste rápido de cantidad leen el documento del servidor dentro de la transacción antes de decidir, y los códigos secuenciales se sacan de meta/counters también transaccionalmente, de modo que dos teléfonos vendiendo a la vez no pisan el mismo número ni sobrevenden. Ventas y fiados guardan un snap del producto (grupo, nombre, precio) para congelar el histórico. La lógica pura vive en src/lib: format.js concentra formato de dinero, fechas ancladas a America/Caracas con Intl, el catálogo base, las reglas de emoji por producto y el cálculo de saldo y semáforo de fiados; cashclose.js concentra los cómputos de cierre y rango, el texto de WhatsApp y la construcción de los PDF. La seguridad se apoya en firestore.rules, que resuelve el rol leyendo el documento del usuario y reserva a admin las eliminaciones de ventas y fiados y la escritura de perfiles. No hay Cloud Functions: el proyecto vive en el plan Spark y toda la lógica es cliente + reglas.
Siete cosas que se rompieron primero
Se rompía · 01
Dos personas registrando ventas desde teléfonos distintos podían sobrevender la última unidad o repetir el mismo código de venta.
Se resolvió así
Cada venta, fiado, abono, anulación y ajuste de stock se ejecuta dentro de runTransaction: se relee el documento de inventario en el servidor, se valida la existencia y recién ahí se escribe la venta y se descuenta. Los códigos PROD-/V-/F- salen de una transacción propia sobre meta/counters.
Se rompía · 02
El internet de la tienda se cae y las ventas no pueden esperar.
Se resolvió así
Firestore se inicializa con persistentLocalCache y persistentMultipleTabManager, y la autenticación con browserLocalPersistence. La app sigue registrando contra la caché local y sincroniza al recuperar señal, sin que la vendedora tenga que hacer nada.
Se rompía · 03
Con serverTimestamp pendiente, las ventas recién registradas tenían createdAt en null y desaparecían de las listas y los totales del día durante unos instantes.
Se resolvió así
Cada documento guarda además createdAtLocal con el ISO del cliente, y los helpers whenDate/whenKey/whenMonth caen a ese campo cuando el timestamp del servidor todavía no llegó.
Se rompía · 04
Una venta a las once de la noche podía contarse en otro día (u otro mes fiscal) si el teléfono estaba configurado en otra zona horaria.
Se resolvió así
Todas las claves contables se derivan con Intl.DateTimeFormat fijado a America/Caracas, de modo que el día y el mes del reporte no dependen del reloj del dispositivo.
Se rompía · 05
El plan Spark no permite activar el proveedor Email/Contraseña por API gratuita, y había que crear los dos usuarios con sus roles sin trabajo manual repetido.
Se resolvió así
scripts/setup-auth.mjs automatiza todo lo automatizable: intenta habilitar el proveedor vía Identity Toolkit Admin API con el token de gcloud, crea (o reconoce si ya existen) las dos cuentas y escribe sus documentos de rol y el contador inicial vía Firestore REST. Es idempotente y se puede reejecutar. Solo queda un clic en consola documentado en el README.
Se rompía · 06
El desplegable nativo con 54 productos era inusable en un teléfono para registrar una venta rápida.
Se resolvió así
Se construyó un SearchSelect propio: un combobox con buscador integrado, cierre por click fuera y touchstart, lista de 240px con scroll táctil y opción resaltada, que filtra por nombre y por grupo mientras se escribe.
Se rompía · 07
La vendedora necesitaba operar todo el día pero no debía ver ganancias ni el resumen financiero.
Se resolvió así
El rol de users/{uid} define dos juegos de pestañas y dos vistas distintas del mismo dato: la vendedora entra directo a Inventario y no tiene acceso a Resumen ni SENIAT, y las reglas de Firestore reservan las eliminaciones a admin. El README documenta explícitamente el límite de este enfoque y la alternativa de mover los costos a una colección solo-admin.
Los colores de la tienda
Boutique cálida y femenina en modo diurno: fondo rosa empolvado, tarjetas blancas de esquinas muy redondeadas, rosa fucsia como color de acción, azul marino profundo reservado para el panel de marca y detalles dorados de vitrina. Titulares en Fraunces serif, interfaz en Poppins. Semántica de color viva para el semáforo de fiados: verde al día, ámbar por vencer, rojo vencido.
primary
#d4567a
secondary
#14142b
accent
#c9a35a
bg
#fbf2f4
surface
#ffffff
text
#1c1830
Degradado de marca
/Users/macbook/boutique-conny-app/src/theme.css (bloque :root — --rose #d4567a, --rose-d #b8385f, --navy #14142b, --gold #c9a35a, --bg #fbf2f4, --card #ffffff, --ink #1c1830, --pink #ec7f9b, --cream #f4ead2)
Sin capturas publicables: es la herramienta interna de una tienda con datos de clientas reales. Todas las pantallas de esta página están recreadas en HTML y CSS a partir de la interfaz real.
Aplicación web para una boutique multimarca real que se llevaba en cuaderno: registrar lo que entra, lo que se vende, lo que se fía y cuánto quedó en caja al final del día.
Dos roles con vistas distintas sobre los mismos datos: la vendedora (Marisabel) ve inventario, ventas, fiados y cierre; la administradora (Conny) ve además el resumen financiero con gráficos y el módulo contable para el SENIAT.
Todo corre sobre Cloud Firestore en tiempo real con caché persistente: si se cae el internet en la tienda, la app sigue registrando y sincroniza sola al volver la señal.
El cierre de caja diario genera un PDF con jsPDF y abre WhatsApp con el informe ya redactado hacia el teléfono de la dueña, que es como realmente circula la información en el negocio.
Boutique Conny
¿Quieres algo así para tu producto?
Una tienda de barrio operando con transacciones atómicas, caché offline y contabilidad anclada a su zona horaria. Si tienes un negocio que todavía vive en un cuaderno, este es exactamente el terreno que conozco.