Skip to content

Frontend Applications Overview

Upvendo has three separate Vue 3 frontend applications, each targeting a different user persona and use case. All three communicate with the backend through the Cloudflare Workers proxy.


Application Summary

AppRepositoryFrameworkUI LibraryState ManagementTarget User
Backofficeupvendo-backofficeVue 3Vuetify 3VuexMerchant staff
Kioskupvendo-kioskVue 3Tailwind CSSPiniaEnd customers (self-service)
Online Orderingupvendo-online-orderingVue 3Tailwind CSSPiniaEnd customers (web)

Backoffice Application

Purpose: The merchant management dashboard. Used by business owners, managers, and staff to configure their restaurants, manage menus, view transactions, handle customers, and manage all aspects of their Upvendo setup.

Technology Stack

ComponentTechnology
FrameworkVue 3 (Composition API + Options API mix)
UI LibraryVuetify 3 (Material Design)
State ManagementVuex 4 (modular stores)
RoutingVue Router 4
HTTP ClientAxios
PermissionsCASL (Attribute-Based Access Control)
Mobile WrapperCapacitor (iOS/Android)
Build ToolVite

Architecture Patterns

Vuex Modular Store: The Vuex store is split into domain-specific modules that mirror the backend service structure:

  • auth -- Authentication state, JWT token management
  • vendor -- Current vendor data
  • location -- Active location selection
  • menu -- Menu management
  • item -- Item/product management
  • category -- Category management
  • modifier -- Modifier and modifier group management
  • transaction -- Transaction/order data
  • customer -- Customer management
  • device -- Device management
  • settings -- Application settings
  • loyalty -- Loyalty program configuration
  • offer -- Offer management

Each module typically includes: state, mutations, actions (for API calls), and getters.

CASL Permissions: The backoffice uses CASL for fine-grained permission checking. Permissions are loaded from the backend based on the user's roles and define what actions (create, read, update, delete) are allowed on which resources.

Permissions flow:

  1. User logs in -> JWT token received
  2. Permissions fetched from backend based on user roles
  3. CASL ability instance created with permission rules
  4. Components use v-if="can('update', 'Item')" to conditionally show/hide UI
  5. Route guards check permissions before navigation

Multi-Location Support: The backoffice supports multi-location merchants. The active location is stored in state and many API calls include a location_id parameter. Location switching updates the store and refreshes data.

Capacitor Mobile Wrapper: The backoffice is wrapped in Capacitor for iOS and Android deployment. The CapacitorApiKey header is sent with API requests for device identification. Native features accessed through Capacitor include:

  • Push notifications (Firebase)
  • Camera access (for photo studio)
  • Biometric authentication
  • App update management

API Integration

All API calls go through an Axios instance configured with:

  • Base URL pointing to the Cloudflare Workers proxy
  • JWT bearer token in Authorization header
  • X-Capacitor-API-Key header for mobile clients
  • Response interceptors for 401 handling (token refresh or logout)
  • Request interceptors for adding tenant context

Key Features

  • Menu and item management (CRUD, drag-and-drop ordering)
  • Transaction history and reporting
  • Customer management and loyalty programs
  • Device and kiosk management
  • Branding and visual customization
  • Multi-language content management
  • Third-party integration configuration (Square, Deliveroo, UberEats, Shopify)
  • Photo studio for item images
  • Team member management with role-based permissions
  • Tax rate and billing configuration
  • Guided setup wizard for new merchants

Kiosk Application

Purpose: Self-service ordering application running on kiosk hardware (tablets/touchscreens) in physical restaurant locations. Customers use it to browse the menu, customize orders, and pay.

Technology Stack

ComponentTechnology
FrameworkVue 3 (Composition API)
UI LibraryTailwind CSS
State ManagementPinia
RoutingVue Router 4
HTTP ClientAxios
Mobile WrapperCapacitor (Android primarily)
Device SDKCustom device SDK integration
Push NotificationsFirebase Cloud Messaging
Build ToolVite

Architecture Patterns

Pinia Stores: The kiosk uses Pinia for state management, organized by domain:

  • useAuthStore -- Device authentication state
  • useCartStore -- Shopping cart with items, modifiers, quantities
  • useMenuStore -- Active menu, display groups, items
  • useLocationStore -- Current location data, business hours
  • useSettingsStore -- Kiosk configuration, branding, language
  • usePaymentStore -- Payment flow state
  • useOrderStore -- Order submission and tracking
  • useLoyaltyStore -- Customer loyalty points and rewards
  • useStockStore -- Real-time stock availability (via D1 proxy)

Device-First Design: The kiosk is designed for touchscreen interaction:

  • Large touch targets
  • No keyboard input (on-screen keyboard for customer info)
  • Idle timeout with screensaver/attract mode
  • Hardware integration (payment terminals, receipt printers)
  • Custom tap recognizer (v-kiosk-tap) for all interactive elements -- Android WebView suppresses click events on long presses, so the kiosk app replaces @click with a custom Vue directive that listens to raw touch events. See Kiosk Tap Directive for the full rationale and rules.

Cart Management: The cart store handles:

  • Adding/removing items with modifier selections
  • Quantity adjustments
  • Stock reservation via the D1 proxy (prevents overselling)
  • Price calculation including modifiers, taxes, and discounts
  • Loyalty reward application
  • Dining option selection (for here / takeout)

Payment Flow: Payment integration varies by terminal:

  • Stripe Terminal (physical card reader)
  • Viva Wallet Terminal
  • The payment store orchestrates the terminal communication through the backend

Firebase Integration: Used for:

  • Push notifications for order status updates
  • Remote configuration updates
  • Analytics

API Integration

The kiosk communicates through two proxy paths:

  1. /api/* -- Standard backend API calls for authentication, menu data, order submission
  2. /d1-api/* -- Direct D1 calls for real-time stock checking and reservation

Stock flow:

  1. Menu loads -> stock levels fetched from D1 for all items
  2. Customer adds item to cart -> reservation created in D1
  3. Customer completes order -> reservations released, stock decremented
  4. Customer abandons cart -> reservations expire after 10 minutes

Key Features

  • Full menu browsing with categories and display groups
  • Item detail view with allergen/ingredient information
  • Modifier selection with pricing
  • Variant group selection (e.g., sizes)
  • Shopping cart with real-time stock validation
  • Loyalty program integration (scan/enter loyalty card)
  • Multiple dining options (for here, takeout)
  • Integrated payment terminal support
  • Receipt printing (Star Micronics)
  • Multi-language support (language selection at start)
  • Idle timeout and attract mode
  • Accessibility considerations for touchscreen use

Online Ordering Application (Zestidoo)

Purpose: Customer-facing web application for online ordering. Customers access it through custom branded domains (e.g., zestidoo.be/merchant-slug) to place delivery or pickup orders.

Technology Stack

ComponentTechnology
FrameworkVue 3 (Composition API)
UI LibraryTailwind CSS
State ManagementPinia
RoutingVue Router 4
HTTP ClientAxios
Build ToolVite

Architecture Patterns

Multi-Merchant Routing: The online ordering app serves multiple merchants from a single deployment. Merchant identification works through:

  1. Slug-based routing: URLs contain the merchant slug (e.g., /acme-restaurant/menu)
  2. Domain-based routing: Custom domains resolve to specific merchants
  3. Tenant resolution: The SetTenantDatabase:online-ordering middleware on the backend uses the slug to find the correct tenant database

Pinia Stores:

  • useMerchantStore -- Current merchant data, branding, locations
  • useMenuStore -- Active menu, categories, items
  • useCartStore -- Shopping cart management
  • useCustomerStore -- Customer authentication and profile
  • useOrderStore -- Order placement and tracking
  • usePaymentStore -- Online payment flow (Stripe)
  • useLocationStore -- Selected location, delivery zones, business hours

Branding System: Each merchant has a custom visual identity applied at runtime:

  • Primary/secondary colors from BrandingProfile
  • Logo and cover images from Cloudflare Images
  • Custom fonts (if configured)
  • The branding is loaded during merchant resolution and applied as CSS variables

Order Flow:

  1. Customer arrives at merchant page (via slug or custom domain)
  2. Location selection (if merchant has multiple locations)
  3. Menu browsing with real-time availability
  4. Cart building with modifier selection
  5. Dining option selection (delivery / pickup)
  6. Customer authentication (login or guest checkout)
  7. Delivery address entry (for delivery orders)
  8. Payment via Stripe
  9. Order confirmation with real-time status tracking

API Integration

The online ordering app primarily uses /api/* through the proxy. Key differences from backoffice/kiosk API usage:

  • No JWT for initial load -- Menu and merchant data is loaded without authentication
  • Slug-based tenant resolution -- The merchant slug is passed in the URL, not in a JWT
  • Customer authentication -- Separate auth flow (phone + OTP, not email + password)
  • Stock via proxy -- Stock availability comes through the D1 proxy for real-time data

Multi-Domain Deployment

The app is deployed to multiple domains per country:

DomainCountry
zestidoo.comGlobal
zestidoo.beBelgium
zestidoo.nlNetherlands
zestidoo.frFrance
zestidoo.deGermany
zestidoo.co.ukUK

Each domain serves the same application but may have country-specific defaults (currency, language, etc.).

Key Features

  • Merchant landing page with branding
  • Location selection with map view
  • Full menu browsing with search and categories
  • Item detail with allergens, ingredients, dietary info
  • Modifier and variant selection
  • Shopping cart with subtotal calculation
  • Guest checkout or authenticated ordering
  • Delivery address management with geocoding
  • Pickup time selection
  • Stripe online payment
  • Order tracking with real-time status updates
  • Order history for authenticated customers
  • Multi-language support
  • Responsive design (mobile-first)
  • SEO-friendly rendering

Shared Patterns Across All Frontends

API Layer Architecture

All three apps follow the same API integration pattern:

  1. Axios instance with base URL pointing to the Cloudflare Workers proxy
  2. Request interceptors for adding authentication headers
  3. Response interceptors for handling 401 (unauthorized) responses
  4. Service modules that wrap API calls with typed parameters and return values

Multi-Language Support

All apps support multiple languages:

  • Language data is fetched from the backend per tenant
  • Items, categories, and menus use the details object with language-keyed names/descriptions
  • The UI itself uses Vue i18n (or equivalent) for static strings
  • Language selection is stored in local state

Branding Integration

Each app loads branding from the backend:

  • Colors (primary, secondary, accent)
  • Logo images (via Cloudflare Images with variant presets)
  • The kiosk and online ordering apps apply branding as CSS custom properties
  • The backoffice shows branding previews in the settings UI

Error Handling

Common error handling approach:

  • Network errors show toast/snackbar notifications
  • Validation errors are mapped to form field errors
  • 401 responses trigger re-authentication flow
  • 500 errors show generic error messages with retry options

Environment Configuration

All apps use Vite environment variables for configuration:

  • VITE_API_BASE_URL -- Points to the correct proxy environment
  • VITE_ENVIRONMENT -- Current environment (production, staging, testing)
  • Environment-specific .env files for each deployment target

Key Patterns for AI Bug Fixing

  1. Check which state management system the app uses. Backoffice uses Vuex (mutations + actions), while Kiosk and Online Ordering use Pinia (direct state mutation in actions).

  2. Permission bugs in the backoffice often relate to CASL ability definitions. Check the permission rules loaded from the backend and the can() checks in components.

  3. Stock discrepancies between what the customer sees and actual availability usually involve the D1 proxy layer. Check if stock reservations are being created/released correctly.

  4. Multi-tenant context in online ordering depends on the slug being correct. If a customer sees the wrong merchant's data, check the slug resolution in the route and the SetTenantDatabase:online-ordering middleware.

  5. Branding not loading usually means the BrandingProfile is missing or the Cloudflare Image ID is invalid. Check the vendor's branding_profile_id and the tenant database.

  6. Payment flow failures span multiple systems (frontend store -> proxy -> backend -> Stripe/Viva). Trace the flow from the payment store action through to the backend webhook handler.

  7. Capacitor-specific bugs in the backoffice only occur on mobile. Check for Capacitor.isNativePlatform() guards and ensure native plugin calls are wrapped in try/catch.