Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/AndresLopezCorrales/La-casa-del-bordadito/llms.txt

Use this file to discover all available pages before exploring further.

La Casa del Bordadito splits its persistence across two Firebase products. Firestore holds catalogue and transactional data that benefits from flexible querying (café items, shopping carts, orders). Firebase Realtime Database stores data that requires low-latency sync or presence signals (users, chat messages, unread counters, embroidery patterns, workshop info). Both databases share the same Firebase project and use the authenticated user’s UID as the primary linking key.

Firestore collections

Each document represents a single item on the café menu. The document ID is auto-generated by Firestore.Collection path: cafes
{
  "id": "auto-generated-doc-id",
  "nombre": "Cappuccino",
  "descripcion": "Espresso con leche vaporizada",
  "imagenUrl": "https://firebasestorage.googleapis.com/...",
  "categoria": "Bebidas Calientes",
  "tamano": {
    "Pequeño": 35.0,
    "Mediano": 45.0,
    "Grande": 55.0
  },
  "isActive": true
}
FieldTypeDescription
idStringMirrors the Firestore document ID; populated after read
nombreStringDisplay name of the item
descripcionStringShort description
imagenUrlStringFirebase Storage download URL
categoriaStringFilter category (e.g. “Bebidas Calientes”, “Postres”)
tamanoMap<String, Double>Available sizes mapped to price in MXN
isActiveBooleanWhether the item is visible to customers
The @PropertyName("isActive") annotation on the Cafe data class ensures Firestore serializes this field with the exact key isActive rather than inferring it from the Kotlin property name.
Each authenticated user gets their own sub-collection of cart items. Items are stored as individual documents rather than an array so that quantity updates and deletions target a single document without re-writing the entire cart.Collection path: carritos/{uid}/itemsDocument ID format: {cafeId}_{tamano} — for example, abc123_Mediano. This composite key prevents duplicates: adding the same item and size a second time increments cantidad on the existing document instead of creating a new one.
{
  "carritoItemId": "abc123_Mediano",
  "nombre": "Cappuccino",
  "tamano": "Mediano",
  "precio": 45.0,
  "cantidad": 2,
  "imagenUrl": "https://firebasestorage.googleapis.com/..."
}
FieldTypeDescription
carritoItemIdStringSame as document ID ({cafeId}_{tamano})
nombreStringItem display name (denormalised from cafes)
tamanoStringSize selected by the user
precioDoubleUnit price at time of adding to cart
cantidadIntQuantity; minimum 1
imagenUrlStringItem image URL (denormalised from cafes)
CarritoItem implements Serializable so instances can be passed between activities via Intent extras.
When a customer confirms checkout, a new document is written to ordenes. The document ID is auto-generated. Admins query this collection to view all orders; users filter by usuario field.Collection path: ordenes
{
  "id": "auto-generated-doc-id",
  "usuario": "user@example.com",
  "total": "90.0 MXN",
  "fecha": 1700000000000,
  "metodoPago": "Tarjeta (Visa ****4242)",
  "items": [
    {
      "carritoItemId": "abc123_Mediano",
      "nombre": "Cappuccino",
      "tamano": "Mediano",
      "precio": 45.0,
      "cantidad": 2,
      "imagenUrl": "https://..."
    }
  ]
}
FieldTypeDescription
idStringMirrors the Firestore document ID
usuarioStringEmail address of the ordering user
totalStringFormatted total string, e.g. "90.0 MXN"
fechaLongUnix epoch milliseconds — use Constantes.obtenerFechaHora() to format
metodoPagoStringHuman-readable payment description
itemsList<CarritoItem>Snapshot of the cart at checkout time
Orden implements Serializable so it can be passed to HistorialDetalleActivity as an Intent extra.

Realtime Database nodes

Each user’s profile is written to Realtime Database at sign-up and updated on profile edits. The node key is the Firebase Auth UID.Path: Usuarios/{uid}
{
  "uid": "firebase-auth-uid",
  "email": "user@example.com",
  "nombres": "Ana García",
  "urlImagenPerfil": "https://firebasestorage.googleapis.com/...",
  "codigoTelefono": "+52",
  "telefono": "5512345678",
  "proveedor": "Email",
  "escribiendo": "",
  "tiempo": 1700000000000,
  "fecha_nac": "01/01/1990",
  "esAdmin": false,
  "esSoporte": false,
  "online": true
}
FieldTypeDescription
uidStringFirebase Auth UID
emailStringUser’s email address
nombresStringDisplay name
urlImagenPerfilStringProfile photo URL (Firebase Storage) or multiavatar SVG URL
codigoTelefonoStringInternational dial code (e.g. "+52")
telefonoStringPhone number (without dial code)
proveedorStringAuth provider: "Email" or "Google"
escribiendoStringTyping-indicator flag; set to the recipient UID while the user is typing
tiempoLongUnix epoch milliseconds of account creation
fecha_nacStringDate of birth in "dd/MM/yyyy" format
esAdminBooleantrue if the account has admin privileges
esSoporteBooleantrue if the account is a support agent
onlineBooleanPresence flag — managed by ApplicationClass.actualizarEstadoOnline()
The online field uses Firebase’s onDisconnect() handler to fall back to false if the client loses connectivity without explicitly signing out. Note that urlImagenPerfil is the database key used in all read/write paths; the Usuario.kt data class maps this to its imagen property only for model-level deserialization.
All messages between two users live under a shared, deterministic room key (see rutaChat below). Each message is a child node keyed by a push ID generated with FirebaseDatabase.push().Path: Chats/{rutaChat}/{msgId}
{
  "idMensaje": "-NxPush123",
  "tipoMensaje": "TEXTO",
  "mensaje": "Hola, ¿tienen cappuccino hoy?",
  "emisorUid": "uid-of-sender",
  "receptorUid": "uid-of-recipient",
  "tiempo": 1700000000000
}
FieldTypeDescription
idMensajeStringFirebase push ID
tipoMensajeString"TEXTO" or "IMAGEN" (from Constantes)
mensajeStringMessage text, or a Firebase Storage URL when tipoMensaje = "IMAGEN"
emisorUidStringUID of the message sender
receptorUidStringUID of the intended recipient
tiempoLongUnix epoch milliseconds
Tracks how many unread messages each user has received from each sender. The outer key is the recipient’s UID; the inner key is the sender’s UID; the value is an integer count.Path: ChatsUnreadCount/{uid}/{senderUid}
ChatsUnreadCount/
└── uid-of-recipient/
    ├── uid-of-sender-A: 3
    └── uid-of-sender-B: 1
When a user opens a chat with a given sender, the counter for that sender is reset to 0. MainActivity listens to the recipient’s node to trigger the in-app popup notification.
Each embroidery pattern is stored as a node under patrones. The matriz field encodes the pixel grid as a list of rows, where each cell contains a color ID from the paleta list (or 0 for unfilled).Path: patrones/{id}
{
  "id": "patron-001",
  "nombre": "Flor de México",
  "columnas": 20,
  "filas": 20,
  "urlImagen": "https://firebasestorage.googleapis.com/...",
  "paleta": [
    { "id": 1, "hex": "#E63946", "nombre": "Rojo Vivo" },
    { "id": 2, "hex": "#2A9D8F", "nombre": "Verde Jade" }
  ],
  "matriz": [
    [0, 0, 1, 2, 0],
    [0, 1, 2, 1, 0]
  ],
  "porcentaje": 0,
  "activo": true
}
FieldTypeDescription
idStringUnique pattern identifier
nombreStringDisplay name
columnas / filasIntGrid dimensions
urlImagenStringPreview image URL
paletaList<ColorPatron>Array of { id, hex, nombre } colour definitions
matrizList<List<Int>>2-D grid — each cell holds a ColorPatron.id or 0
porcentajeIntCompletion percentage (aggregate, not per-user)
activoBooleanWhether the pattern is visible to users
Per-user progress is stored nested within the pattern node, keyed by UID. The ProgresoUsuario model maps each painted cell:
data class ProgresoUsuario(
    val puntosPintados: Map<String, Int> = emptyMap()
    // Key:   "fila_columna"  e.g. "3_7"
    // Value: ColorPatron.id  e.g. 1
)
A key of "3_7" means row 3, column 7. This flat-map approach avoids nested arrays and lets Realtime Database rules target individual cells.
Stores metadata about the craft workshop offerings. Currently used to publish a promotional flyer URL that TallerActivity loads via Glide.Path: TallerInfo/flyerUrl
PathTypeDescription
TallerInfo/flyerUrlStringFirebase Storage URL of the workshop flyer image

rutaChat: deterministic room key generation

Any two users always resolve to the same chat room path, regardless of who initiates the conversation. Constantes.rutaChat() achieves this by sorting both UIDs alphabetically and joining them with an underscore:
fun rutaChat(receptorUid: String, emisorUid: String): String {
    val arrayUid = arrayOf(receptorUid, emisorUid)
    Arrays.sort(arrayUid)
    return "${arrayUid[0]}_${arrayUid[1]}"
}
Example: if user "uid-B" messages user "uid-A", and "uid-A" later replies, both calls produce the same path "uid-A_uid-B" because "uid-A" < "uid-B" lexicographically. This eliminates the need for a separate room-lookup index.

Payment methods

Payment cards are stored locally in Realtime Database under the user’s own profile node (not in a payment processor). The PaymentMethod model represents a tokenized card:
data class PaymentMethod(
    var id: String         = "",
    var last4: String      = "",
    var brand: String      = "",   // "VISA", "MASTERCARD", "AMEX", "DISCOVER"
    var holderName: String = "",
    var expiry: String     = "",   // "MM/AA"
    var isDefault: Boolean = false
) {
    fun maskedNumber() = "**** **** **** $last4"
}
Full card numbers are never stored. Only the last four digits, brand, holder name, and expiry are persisted. The EditCredit library handles masked input formatting in TarjetaAgregarActivity.

Build docs developers (and LLMs) love