Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/luisumit/LaPreviaRestobar/llms.txt

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

The Waiter (Mesero) dashboard is the primary point-of-sale interface for floor staff. It follows an offline-first architecture: all tables, products, and orders are cached in Room so the waiter can continue working when Firebase is unreachable, and pending orders are synced automatically as soon as internet connectivity is restored. The dashboard opens at waiter_main, which renders WaiterMainScreen after AppNavigation verifies that userRole == UserRole.MESERO.

Workflow

1

View Tables

TablesScreen displays all tables in a 2-column LazyVerticalGrid, sorted by table number. A summary card shows total, occupied, free, and active order counts. Tapping a TableCard navigates to table_details/{tableId} where tableId is an integer in the range 1–8.
2

Browse Products and Build an Order

TableDetailsScreen calls viewModel.setCurrentTable(tableId) on load, binding all subsequent cart operations to that table. Active products are rendered as ProductItem cards; tapping one calls viewModel.addItemToCurrentOrder(product). The cart counter badge on the top-bar IconButton updates in real time from currentOrderItems.
3

Confirm and Send to Kitchen

When the waiter taps the cart icon, ConfirmOrderDialog shows a line-item summary with the running total. Confirming calls viewModel.createOrder(tableId, tableNumber), which constructs an Order with status = OrderStatus.ENVIADO and persists it to Room. If internet is available, it is immediately pushed to Firebase and the table is marked OCUPADA via firebaseTableRepository.assignOrderToTable(). If offline, the order is saved with syncStatus = "PENDING" for later sync.
4

Track Order Status

OrdersScreen groups all active orders by status into labelled sections: Enviadas a cocina, Aceptadas, En preparación, and Listas para servir. The waiter also receives real-time notifications via NotificationPanel when the chef changes an order’s status.
5

Deliver and Free the Table

Once an order reaches LISTO, the waiter taps ENTREGAR on the OrderCard, calling viewModel.markOrderAsDelivered(orderId) which sets the status to ENTREGADO. After the client finishes, tapping LIBERAR calls viewModel.markTableAsFree(orderId), which marks the order COMPLETED and resets the table to LIBRE.

Screens

Renders the full table grid and a TablesSummary stat card. Observes viewModel.tables and viewModel.orders. Calls viewModel.refreshData() on first composition. Shows an offline banner if isInternetAvailable == false or isFirebaseConnected == false, with a Reconectar button that calls viewModel.syncWithFirebase().

WaiterViewModel

WaiterViewModel is a @HiltViewModel that owns the entire Mesero data layer. It combines a Room database (offline-first) with Firebase Realtime listeners for live kitchen updates.

StateFlows

StateFlowTypeDescription
tablesStateFlow<List<Table>>All restaurant tables, filtered to IDs 1–8 and status-corrected against active orders
ordersStateFlow<List<Order>>Active orders (excludes COMPLETED and CANCELLED) sourced from Room
productsStateFlow<List<Product>>Full product list, kept live via firebaseProductRepository.listenToProductChanges()
isLoadingStateFlow<Boolean>True while initial data load or manual refresh is in progress
currentOrderItemsStateFlow<List<OrderItem>>Cart contents for the currently selected table
currentTableIdStateFlow<Int?>ID of the table currently being served
isFirebaseConnectedStateFlow<Boolean>Reflects live Firebase reachability
isInternetAvailableStateFlow<Boolean>Set by ConnectivityManager.NetworkCallback
connectionStatusStateFlow<String>Human-readable connectivity string for display in banners
connectionMessageStateFlow<String?>Ephemeral banner text, auto-cleared after 3 s
successMessageStateFlow<String?>Ephemeral success banner text
errorMessageStateFlow<String?>Ephemeral error banner text
notificationsStateFlow<List<Notification>>In-app notification queue, capped at 5 entries

Computed Properties

val currentOrderTotal: Double
    get() = _currentOrderItems.value.sumOf { it.subtotal }

val currentOrderItemCount: Int
    get() = _currentOrderItems.value.sumOf { it.quantity }

val occupiedTablesCount: Int
    get() = _tables.value.count { it.status == TableStatus.OCUPADA }

val readyOrdersCount: Int
    get() = _orders.value.count { it.status == OrderStatus.LISTO }

Use Cases Called

  • firebaseTableRepository.getTables() — initial table load
  • firebaseTableRepository.assignOrderToTable(tableId, orderId) — locks table when order is sent
  • firebaseTableRepository.clearTable(tableId) — frees table on delivery or cancellation
  • firebaseOrderRepository.createOrder(order) — pushes new order to Firebase
  • firebaseOrderRepository.updateOrderStatus(orderId, status) — marks delivered, served, or cancelled
  • firebaseOrderRepository.listenToOrderChanges() — real-time status updates from chef
  • firebaseProductRepository.getSellableProducts() — initial product load
  • firebaseProductRepository.listenToProductChanges() — live stock updates
  • syncManager.syncOrders() — uploads pending Room orders after reconnect

Order Creation

fun createOrder(tableId: Int, tableNumber: Int, notes: String? = null) {
    viewModelScope.launch {
        val items = _currentOrderItems.value
        val order = Order(
            id          = UUID.randomUUID().toString(),
            tableId     = tableId,
            tableNumber = tableNumber,
            items       = items,
            status      = OrderStatus.ENVIADO,
            createdAt   = System.currentTimeMillis(),
            updatedAt   = System.currentTimeMillis(),
            total       = items.sumOf { it.subtotal },
            waiterId    = "mesero_actual",
            waiterName  = "Mesero",
            notes       = notes
        )

        // Always persist to Room first (offline-first)
        db.orderDao().insert(order.toEntity().copy(
            syncStatus = if (_isInternetAvailable.value) "SYNCED" else "PENDING"
        ))

        if (_isInternetAvailable.value) {
            firebaseOrderRepository.createOrder(order)
            firebaseTableRepository.assignOrderToTable(tableId, order.id)
            _successMessage.value = "Pedido enviado a cocina - Mesa $tableNumber"
        } else {
            _successMessage.value = "SIN INTERNET - Pedido guardado localmente"
        }

        refreshOrdersFromRoom()
        clearCurrentOrder()
        loadTables()
    }
}
WaiterMainScreen uses a ScrollableTabRow with three tabs rendered by TabContent:
Tab indexLabelComposable
0MESASTablesScreen
1PRODUCTOSProductsScreen
2ORDENESOrdersScreen
TableDetailsScreen is not a tab — it is reached from TablesScreen via navController.navigate("table_details/${table.id}") and exists as a separate composable route ("table_details/{tableId}") in AppNavigation. InventoryScreen is also registered as a standalone "inventory" route in AppNavigation rather than as a tab inside WaiterMainScreen. On tablets in landscape orientation (screenWidthDp >= 600 and ORIENTATION_LANDSCAPE), the tab row is replaced by a NavigationRail on the left side of the screen, with the content filling the remaining space.

NotificationPanel

NotificationPanel is rendered as a floating overlay anchored to the top-end of the content area when the notification bell icon is tapped. It displays up to 5 entries from viewModel.notifications.
NotificationPanel(
    notifications  = notifications,
    onDismissNotification = { notification ->
        viewModel.removeNotification(notification)
    },
    onClearAll = { viewModel.clearAllNotifications() },
    modifier = Modifier
        .align(Alignment.TopEnd)
        .padding(12.dp)
        .widthIn(max = 420.dp)
        .zIndex(2f)
)
Notifications are generated inside WaiterViewModel.showStatusChangeNotification() whenever the chef advances an order’s status. Each notification type corresponds to a NotificationType enum value:
NotificationTypeTrigger
ORDER_ACCEPTEDChef changes status to ACEPTADO
ORDER_IN_PREPARATIONChef changes status to EN_PREPARACION
ORDER_READYChef changes status to LISTO
ORDER_DELIVEREDStatus changes to ENTREGADO
ORDER_CANCELLEDWaiter calls cancelOrder()
A _sentNotifications: MutableSet<String> keyed by "${orderId}_${status}" prevents duplicate notifications when Firebase emits multiple events for the same state change. Each key is automatically removed after 5 seconds.

Build docs developers (and LLMs) love