Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/teofilobetancourt/Restaurant-Equis/llms.txt

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

Restaurant Equis follows a classic three-tier web architecture: a React 19 single-page application (SPA) handles the presentation layer, a FastAPI service owns the business logic and data access layer, and PostgreSQL persists all restaurant state. In production these three tiers are stitched together by Nginx, which acts as the single public-facing entry point — serving pre-compiled static frontend files for every non-API route and transparently forwarding all /api/* requests to the FastAPI process listening on port 5000. In development, Vite’s dev server replaces Nginx for the frontend while Uvicorn runs the backend directly.

Request Flow

Every user interaction follows the same path through the system:
  1. Browser sends an HTTP request to port 80 (production) or directly to localhost:3000 / localhost:5000 (development).
  2. Nginx inspects the request path:
    • Requests for /api/*, /api/docs, or /api/openapi.json are proxied to http://127.0.0.1:5000.
    • All other requests are served as static files from /var/www/restaurantequis (the compiled Vite bundle).
  3. React SPA handles client-side routing for non-API requests. Unknown paths fall back to index.html via Nginx’s try_files directive, enabling SPA navigation without full-page reloads.
  4. FastAPI receives proxied API requests, validates them with Pydantic, executes the business logic via SQLAlchemy, and returns JSON responses.
  5. PostgreSQL is queried by SQLAlchemy — no raw SQL in application code. The ORM translates Python model operations into optimized SQL and returns mapped objects.

Frontend

The frontend is a TypeScript React 19 SPA built and bundled with Vite 6. Styling is handled entirely by Tailwind CSS 4, loaded through the @tailwindcss/vite plugin — no separate PostCSS configuration is needed. The @vitejs/plugin-react plugin enables fast refresh during development. Key source layout:
src/
├── api/
│   └── index.ts          ← Sole API abstraction layer (all fetch calls)
├── components/
│   ├── Sidebar.tsx        ← Global navigation sidebar
│   ├── TopBar.tsx         ← Header bar with Venezuela-time clock
│   └── Toast.tsx          ← Global notification toasts
└── views/
    ├── PosView.tsx        ← Point of Sale
    ├── KitchenView.tsx    ← Kitchen display
    ├── OrdersView.tsx     ← Orders history
    ├── InventoryView.tsx  ← Inventory management
    ├── SuppliersView.tsx  ← Supplier directory
    └── ReportsView.tsx    ← KPI reports
src/api/index.ts is the only file that talks to the backend. Every view imports from this module instead of making fetch calls directly, making the backend base URL (controlled by the VITE_API_URL environment variable) trivially swappable between local development and production. The Vite config sets @ as a root-level path alias, allows the dev server to bind to 0.0.0.0:3000, and supports a DISABLE_HMR flag to pause file-watching during automated edits.

Backend

The backend entry point is backend/main.py. On startup it runs a lifespan context manager that:
  1. Creates the Inventario PostgreSQL schema with CREATE SCHEMA IF NOT EXISTS.
  2. Calls Base.metadata.create_all() to create all ORM-mapped tables (idempotent — safe to re-run).
  3. Calls seed_initial_data() to populate empty tables with demo records.
The FastAPI application is configured with the Swagger UI served at /api/docs and the OpenAPI JSON at /api/openapi.json. Five routers register all business endpoints under the /api/ prefix:
RouterFileEndpoints
ordenesrouters/ordenes.pyCreate, list, update state, delete orders
productosrouters/productos.pyRead the dish catalogue
inventariorouters/inventario.pyCRUD for supply stock
proveedoresrouters/proveedores.pyCRUD for the supplier directory
reportesrouters/reportes.pyKPI summary and filtered order reports
Schemas in backend/schemas.py define Pydantic v2 models for every request body and response object. Pydantic validates all incoming JSON automatically — invalid payloads are rejected with a structured 422 error before they reach any business logic. ORM models in backend/models.py use SQLAlchemy 2.x declarative syntax and cover all database tables (see Database below). CORS is configured via CORSMiddleware, reading the allowed origin list from the CORS_ORIGINS environment variable.

Database

PostgreSQL 15+ stores all application data across two schemas:

public schema — Core restaurant operations

ColumnTypeNotes
id_mesaIntegerPrimary key, auto-increment
capacidadIntegerSeating capacity
estadoEnumdisponible, ocupada, reservada, fuera_de_servicio
ubicacionString(100)Human-readable location label (e.g., “Terraza - M1”)
ColumnTypeNotes
id_platoIntegerPrimary key, auto-increment
nombreString(100)Dish name
descripcionString(255)Short description
precioNumeric(8,2)Unit price
categoriaEnumentrada, plato_principal, postre, bebida, acompañante
ColumnTypeNotes
cedula_clienteString(20)Primary key — national ID (e.g., “V-12345678”)
nombreString(100)Full name
telefonoString(20)Contact phone
emailString(100)Email address (optional)
direccion_habitualString(255)Usual delivery address (optional)
ColumnTypeNotes
num_ticketIntegerPrimary key, auto-increment
tipo_pedidoEnummesa, pickup, delivery
estado_ordenEnumrecibido, preparando, listo, entregado
id_mesaInteger FKReferences mesa.id_mesa (nullable for pickup/delivery)
cedula_clienteString FKReferences cliente.cedula_cliente
direccion_envioString(255)Delivery address (nullable)
fecha_creacionDateTimeOrder timestamp (UTC)
ColumnTypeNotes
num_ticketInteger FKComposite PK — references pedido.num_ticket
id_platoInteger FKComposite PK — references plato.id_plato
cantidadIntegerQuantity ordered
subtotalNumeric(8,2)Line total (quantity × unit price)
ColumnTypeNotes
num_facturaIntegerPrimary key, auto-increment
num_ticketInteger FKReferences pedido.num_ticket (unique — one invoice per order)
fecha_emisionDateTimeInvoice timestamp (UTC)
subtotalNumeric(8,2)Pre-tax amount
impuestoNumeric(8,2)Tax amount
totalNumeric(8,2)Final total
estado_pagoEnumpendiente, pagado, anulado
metodo_pagoString(30)Payment method (optional)

Inventario schema — Supply-chain management

ColumnTypeNotes
ID_InsumosBigIntegerPrimary key, auto-increment
Nombre_InsumoString(100)Unique supply item name
Unidad_MedidaString(20)Unit of measure (e.g., “Kg”, “Paquete”)
Stock_ActualNumeric(12,4)Current stock level
Stock_MinimoNumeric(12,4)Minimum stock threshold for alerts
Punto_ReordenNumeric(12,4)Reorder point
FK_IDCategoriaBigInteger FKReferences Inventario.Categoria.ID_Categoria (optional)
ColumnTypeNotes
ID_ProveedorBigIntegerPrimary key, auto-increment
Nombre_EmpresaString(150)Company name
Identificación_RIFString(30)Unique tax ID (Venezuelan RIF format)
CiudadString(100)City
Telefono_EmpresaString(30)Company phone
Email_EmpresaString(100)Unique company email
DireccionString(255)Street address
Nombre_EncargadoString(100)Contact person’s name
ColumnTypeNotes
ID_CategoriaBigIntegerPrimary key, auto-increment
Nombre_CategoriaString(100)Unique category name
All tables and the Inventario schema are created automatically by SQLAlchemy at backend startup — no migration tool or manual DDL is required for local development.

Nginx

In production, Nginx acts as the single public entry point on port 80. Its configuration (nginx.conf) implements the following routing rules:
nginx.conf (abbreviated)
server {
    listen 80 default_server;
    root /var/www/restaurantequis;
    index index.html;

    # Swagger UI
    location /api/docs {
        proxy_pass http://127.0.0.1:5000/docs;
    }

    # REST API — proxied to FastAPI on :5000
    location /api/ {
        proxy_pass         http://127.0.0.1:5000;
        proxy_http_version 1.1;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_read_timeout 60s;
    }

    # React SPA — serve static files, fall back to index.html
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Static assets — aggressive 1-year cache
    location /assets/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    gzip on;
    gzip_types text/plain text/css application/javascript application/json image/svg+xml;
}
The try_files $uri $uri/ /index.html rule is what makes client-side routing work — any URL that doesn’t match a static file is served index.html, allowing React Router to handle the route on the client side. Compiled assets under /assets/ receive a one-year Cache-Control: immutable header because Vite injects a content hash into every asset filename.

Environment Variables

Place this file in the project root alongside package.json.
VariableExample ValueDescription
VITE_API_URLhttp://localhost:5000Base URL of the FastAPI backend. Injected into the Vite bundle at build time via import.meta.env.
VITE_WHATSAPP_NUMERO584140000000Restaurant WhatsApp number (country code + number, no symbols). Used by the POS to generate wa.me order summary links.
.env.local
VITE_API_URL=http://localhost:5000
VITE_WHATSAPP_NUMERO=584140000000

Build docs developers (and LLMs) love