Skip to main content

Frameingo - Technical Documentation

Version: 2.0.0
Last Updated: April 2026
Status: Production-ready


Table of Contents​

  1. Executive Summary
  2. Technology Stack
  3. Architecture Overview
  4. Project Structure
  5. Application Flow
  6. Core Components
  7. State Management
  8. Services & Utilities
  9. PWA & Offline Support
  10. Configuration & Settings
  11. Testing
  12. Build & Deployment
  13. Future Considerations (ADRs)

Executive Summary​

This is a TanStack Start-based photobooth web application designed for event photography. It enables users to:

  • Select photo frame templates
  • Capture photos via camera or upload from gallery
  • Reorder and edit images
  • Generate printable photo collages with custom frames
  • Print directly via Bluetooth to Niimbot thermal printers (web) or Android native bridge

The application is built as an offline-capable PWA that can run on kiosks or tablets at events without reliable internet connectivity.

The repo is a monorepo containing:

  • apps/web - Customer-facing PWA (TanStack Start + Cloudflare Workers)
  • apps/internal - Admin panel for managing customers, subscriptions, payments
  • apps/mobile - Native Android app with WebView + Bluetooth printer bridge
  • services/api - Backend API (Cloudflare Workers + D1)
  • packages/shared - Shared TypeScript types for JS↔Android bridge

Technology Stack​

Core Framework​

  • TanStack Start 1.x - Full-stack React framework with SSR
  • React 18.x - UI library
  • TypeScript 5.x - Type safety

Styling & UI​

  • Tailwind CSS 3.x - Utility-first CSS framework
  • Radix UI - Accessible component primitives
    • @radix-ui/react-dialog
    • @radix-ui/react-select
    • @radix-ui/react-slider
    • @radix-ui/react-switch
    • @radix-ui/react-tabs
  • Framer Motion - Animation library
  • Lucide React - Icon library
  • clsx & tailwind-merge - Class utilities

State Management​

  • React Context API - Built-in state management
  • Session Storage - Ephemeral image data
  • Local Storage + IndexedDB - Persistent settings

PWA & Service Workers​

  • @serwist/next - Service worker integration for Next.js
  • @serwist/precaching - Precaching for offline support

Bluetooth Printing​

  • @mmote/niimbluelib - Niimbot thermal printer Bluetooth library

Drag & Drop​

  • @dnd-kit/core - Drag and drop primitives
  • @dnd-kit/sortable - Sortable list components

QR Code​

  • qrcode - QR code generation
  • html5-qrcode - QR code scanning

Fonts​

  • @fontsource/inter
  • @fontsource/lato
  • @fontsource/montserrat
  • @fontsource/open-sans
  • @fontsource/roboto

Development & Build​

  • ESLint - Code linting
  • Prettier - Code formatting
  • Playwright - E2E testing
  • Wrangler - Cloudflare Pages deployment

Architecture Overview​

Billing and Access Architecture​

The backend now separates commercial access into four domains:

  • payments for transaction records and provider status
  • subscriptions for recurring lifecycle state
  • entitlements as the source of truth for access windows
  • licensing for stable public API responses such as /api/v1/license/verify/:key

This allows the app to support both:

  • one-time activation with perpetual entitlement
  • monthly subscriptions with recurring entitlement windows

High-Level Architecture​

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Next.js Application β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Pages Router (Legacy) β”‚ App Router (New) β”‚
β”‚ - /pages/*.tsx β”‚ - /app/* β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ React Context Providers β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Settings β”‚ β”‚ Printer β”‚ β”‚ Voucher β”‚ β”‚
β”‚ β”‚ Provider β”‚ β”‚ Provider β”‚ β”‚ Provider β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Collage β”‚ β”‚
β”‚ β”‚ Provider β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Services Layer β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Storage β”‚ β”‚ Image β”‚ β”‚ Bluetooth β”‚ β”‚
β”‚ β”‚ (IndexedDB) β”‚ β”‚ Processing β”‚ β”‚ Printing β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Service Worker (Serwist) β”‚
β”‚ - Precaching for offline β”‚
β”‚ - Runtime caching strategies β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Architectural Decisions​

  1. Pages Router: Uses Next.js Pages Router (not App Router) for simpler integration with Serwist service worker
  2. React Context: Lightweight state management without external libraries like Redux
  3. Client-Side Rendering: Most pages use client-side features (Bluetooth, camera, storage)
  4. Hybrid Storage: IndexedDB with localStorage fallback for settings persistence

Project Structure​

frameingo-project/
β”œβ”€β”€ apps/
β”‚ β”œβ”€β”€ web/ # Customer-facing PWA (TanStack Start)
β”‚ β”‚ β”œβ”€β”€ src/
β”‚ β”‚ β”‚ β”œβ”€β”€ routes/ # TanStack Route files
β”‚ β”‚ β”‚ β”œβ”€β”€ components/ # Shared components
β”‚ β”‚ β”‚ β”œβ”€β”€ features/ # Feature modules
β”‚ β”‚ β”‚ β”œβ”€β”€ hooks/ # Custom React hooks
β”‚ β”‚ β”‚ β”œβ”€β”€ lib/ # Utilities
β”‚ β”‚ β”‚ β”œβ”€β”€ providers/ # Context providers
β”‚ β”‚ β”‚ └── store/ # Zustand stores
β”‚ β”‚ └── package.json
β”‚ β”‚
β”‚ β”œβ”€β”€ internal/ # Admin panel (Vite + React)
β”‚ β”‚ β”œβ”€β”€ src/
β”‚ β”‚ β”‚ β”œβ”€β”€ pages/ # Admin pages (customers, subscriptions, payments)
β”‚ β”‚ β”‚ β”œβ”€β”€ lib/ # API client
β”‚ β”‚ β”‚ └── components/ # UI components
β”‚ β”‚ └── package.json
β”‚ β”‚
β”‚ └── mobile/ # Native Android app
β”‚ β”œβ”€β”€ app/
β”‚ β”‚ └── src/main/
β”‚ β”‚ β”œβ”€β”€ java/ # Kotlin code (MainActivity, Bluetooth service)
β”‚ β”‚ └── res/ # Android resources
β”‚ └── build.gradle.kts
β”‚
β”œβ”€β”€ services/
β”‚ └── api/ # Backend API (Cloudflare Workers)
β”‚ β”œβ”€β”€ core/ # Core utilities, middleware, repositories
β”‚ β”œβ”€β”€ customers/ # Customer management
β”‚ β”œβ”€β”€ payments/ # Payment processing
β”‚ β”œβ”€β”€ subscriptions/ # Subscription management
β”‚ └── admin/ # Admin API controllers
β”‚
β”œβ”€β”€ packages/
β”‚ └── shared/ # Shared TypeScript types
β”‚ └── src/
β”‚ └── bridge.ts # JS↔Android bridge contract
β”‚
β”œβ”€β”€ docs/ # Documentation
β”œβ”€β”€ scripts/ # Build/deploy scripts
└── package.json # Workspace root (pnpm/npm workspaces)

Key Changes from v1.0​

  • Monorepo structure - Multiple apps in one repo
  • TanStack Start - Replaced Next.js with TanStack Start for full-stack React
  • TanStack Router - File-based routing with type safety
  • Cloudflare Workers - Edge deployment for API
  • Android app - Native WebView wrapper with ESCPOS Bluetooth printing β”œβ”€β”€ docs/ # Architecture decision records β”œβ”€β”€ next.config.mjs # Next.js configuration β”œβ”€β”€ tailwind.config.ts # Tailwind configuration β”œβ”€β”€ tsconfig.json # TypeScript configuration β”œβ”€β”€ Dockerfile # Docker build └── package.json # Dependencies

---

## Application Flow

### Main User Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ HOME │────▢│ FRAME SELECTION │────▢│ IMAGE SELECTION β”‚ β”‚ (index) β”‚ β”‚ (frame-id) β”‚ β”‚ (camera/gallery) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β–Ό β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ IMAGE UPLOAD β”‚ β”‚ β”‚ (capture/select) β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ HOME │◀────│ IMAGE FINAL │◀────│ IMAGE EDITING β”‚ β”‚ (restart) β”‚ β”‚ (print/preview) β”‚ β”‚ (reorder/filter) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜


### Detailed Page Flow

1. **Home Page (`/`)**
- Entry point with customizable start button
- Shows "Connect Printer" button if printer enabled
- Clears previous session verification

2. **Frame Selection (`/frame-selection`)**
- Displays available frame templates (1-4 photos)
- Shows voucher scan if voucher system enabled
- Passes frame ID to next page

3. **Image Selection (`/image-selection`)**
- Choose between camera or gallery upload
- Detects camera availability

4. **Image Upload (`/image-upload`)**
- Camera mode: Live preview with countdown capture
- Gallery mode: File picker for local images
- Supports multiple images based on frame selection
- Stores images in sessionStorage

5. **Image Editing (`/image-editing`)**
- Drag-and-drop reordering via @dnd-kit
- Preview mode with brightness adjustment
- Atkinson dithering filter for thermal printing
- Auto-enters preview mode for single images

6. **Image Final (`/image-final`)**
- Displays final collage with frame applied
- Auto-print if printer connected and enabled
- Manual print/reprint button
- Countdown timer to auto-reset (120s)
- "Done" button returns to home

7. **Customization (`/customization`)**
- PIN-protected settings page (default: 1234)
- Tabs: Page, Frame, Button, Printer, Voucher, Security
- Settings persisted to IndexedDB/localStorage

---

## Core Components

### Layout Components

| Component | Purpose |
| ---------------- | ---------------------------------------------------------- |
| `SharedLayout` | Common page wrapper with header, back button, content area |
| `PageTransition` | Framer Motion page transitions |

### UI Components (Radix-based)

| Component | Description |
| --------- | -------------------------------------- |
| `Button` | Customizable button with theme support |
| `Dialog` | Modal dialogs |
| `Select` | Dropdown selection |
| `Slider` | Range input (brightness control) |
| `Switch` | Toggle (on/off settings) |
| `Tabs` | Tabbed interface for customization |

### Feature Components

| Component | Purpose |
| ------------------ | ------------------------------------------ |
| `Frame` | Frame preview with settings applied |
| `VideoPreview` | Camera live preview + capture |
| `ImageThumbnails` | Multi-image slot display |
| `VoucherScan` | QR code scanner for voucher validation |
| `LockWidget` | PIN entry overlay for customization access |
| `OfflineIndicator` | Network status indicator |
| `InstallPrompt` | PWA installation prompt |

---

## State Management

### Provider Architecture

```text
<App>
β”‚
β”œβ”€ SettingsProvider
β”‚ └─ CustomizationSettings (persisted)
β”‚ β”œβ”€ PageSettings
β”‚ β”œβ”€ FrameSettings
β”‚ β”œβ”€ ButtonSettings
β”‚ β”œβ”€ SecuritySettings (PIN)
β”‚ β”œβ”€ PrinterSettings
β”‚ └─ VoucherSettings
β”‚
β”œβ”€ PrinterProvider
β”‚ β”œβ”€ Bluetooth client state
β”‚ β”œβ”€ Connection status
β”‚ └─ Print functions
β”‚
β”œβ”€ VoucherProvider
β”‚ β”œβ”€ Verification state
β”‚ └─ Verify function
β”‚
└─ CollageProvider
β”œβ”€ Final image (base64)
β”œβ”€ Brightness value
└─ Generate/clear functions

Settings Provider​

File: src/providers/SettingsProvider.tsx

Manages all application settings with persistence:

  • Uses useLocalStorage hook with IndexedDB + localStorage fallback
  • Supports settings migration for schema updates
  • Default values defined in provider

Stored Settings:

  • pageSettings: Background, title, subtitle
  • frameSettings: Header, footer, background, photo frame styling
  • button: Start button text, colors, border styling
  • security: PIN code for admin access
  • printer: Enable/disable, default copies, max copies, paper size
  • voucher: Enable/disable, passcode for voucher access

Printer Provider​

File: src/providers/PrinterProvider.tsx

Handles Bluetooth communication with Niimbot printers:

  • Connect/disconnect via Web Bluetooth API
  • Print images with configurable copies
  • Supports E2E testing mode (NEXT_PUBLIC_E2E=true)
  • Enforces max copies limit from settings

Voucher Provider​

File: src/providers/VoucherProvider.tsx

Manages voucher/PIN-based access control:

  • Validates passcode from QR scan
  • Stores verification state in sessionStorage
  • Clears on new session start

Collage Provider​

File: src/providers/CollageProvider.tsx

Generates final printable image:

  • Combines multiple images into collage based on frame settings
  • Applies Atkinson dithering for thermal printer output
  • Supports brightness adjustment

Services & Utilities​

Storage Service​

File: src/lib/storage.ts

Dual-layer persistent storage:

  1. IndexedDB - Primary storage (PWA-reliable)
  2. localStorage - Fallback, synced with IndexedDB

Key Functions:

  • getItem(key) - Read from storage
  • setItem(key, value) - Write to storage
  • removeItem(key) - Remove a persisted value
  • clear() - Clear all persisted values

Image Processing​

File: src/lib/image-processing.ts

Canvas-based image manipulation:

Functions:

  • generateCollage(images, settings, width, height) - Creates combined image
  • atkinson(imageData, threshold) - Applies Atkinson dithering
  • adjustBrightnessContrastGamma() - Print compensation
  • calculateCropCoordinates() - Aspect ratio preservation

Collage Layouts:

  • 1 image: Full area
  • 2 images: Vertical split (top/bottom)
  • 3 images: 2 top, 1 bottom centered
  • 4 images: 2x2 grid

Custom Hooks​

HookPurpose
useCameraCamera stream management
useImageCapturePhoto capture to canvas
useImageUploadGallery file handling
useImageDimensionsTarget dimensions calculation
useFrameConfigURL param parsing for frame
useOfflineOffline detection
useInstallPromptPWA install handling

PWA & Offline Support​

Service Worker Configuration​

File: src/sw.ts (compiled to public/sw.js)

Uses Serwist for Next.js service worker integration:

Caching Strategies:

Request TypeStrategyCache Name
Static assets (images, fonts)Cache-firststatic-assets-v1
API/JSONCache-firstapi-cache-v1
Navigation (pages)Cache-firstpages-cache-v1

Offline Behavior:

  • All app pages pre-cached
  • Falls back to /offline.html for navigation failures
  • Assets cached on first load

PWA Manifest​

File: public/manifest.json

  • App name: "Photobooth"
  • Standalone display mode
  • Theme color: Black
  • App icons: 48x48 to 512x512

Offline Pages​

  • /offline - React component for offline state
  • /offline.html - Static fallback HTML

Configuration & Settings​

Default Settings​

All default values are defined in src/providers/SettingsProvider.tsx:

// Default button
{
text: "Mulai Sesi",
backText: "Kembali",
bgColor: "#000000",
textColor: "#ffffff",
borderRadius: 8,
font: "Default",
fontSize: "1rem"
}

// Default printer
{
active: true,
name: "NIIMBOT",
defaultCopies: 1,
maxCopies: 10,
paperWidthMM: 30,
paperHeightMM: 50,
dpi: 300
}

// Default security
{
pin: "1234",
iconSize: 24,
iconOpacity: 0
}

Frame Templates​

File: src/data/frames.json

Static frame definitions with IDs mapping to photo counts:

  • 1 β†’ 1 photo
  • 2 β†’ 2 photos
  • 3 β†’ 3 photos
  • 4 β†’ 4 photos

Testing​

Test Structure​

tests/
β”œβ”€β”€ e2e/
β”‚ β”œβ”€β”€ flows/
β”‚ β”‚ β”œβ”€β”€ photobooth.spec.ts # Main flow tests
β”‚ β”‚ β”œβ”€β”€ customization.spec.ts # Settings tests
β”‚ β”‚ β”œβ”€β”€ voucher.spec.ts # Voucher flow tests
β”‚ β”‚ β”œβ”€β”€ pin.spec.ts # PIN access tests
β”‚ β”‚ └── offline.spec.ts # Offline tests
β”‚ β”œβ”€β”€ visual/
β”‚ β”‚ └── snapshots.spec.ts # Visual regression tests
β”‚ β”œβ”€β”€ fixtures/
β”‚ β”‚ └── test-image.png # Test image fixture
β”‚ └── README.md

Running Tests​

# All E2E tests
npm run test:e2e

# Visual snapshot tests (iPad)
npm run test:e2e:visual

# UI mode (interactive)
npm run test:e2e:ui

# Debug mode
npm run test:e2e:debug

Test Configuration​

  • Uses Playwright with Chromium/iPad emulation
  • Base URL: http://localhost:3000
  • E2E mock mode available via NEXT_PUBLIC_E2E=true

Build & Deployment​

Local Development​

# Install dependencies
npm install

# Start dev server (Turbo mode)
npm run dev

# Lint
npm run lint

# Format
npm run format

Production Build​

# Build for production
npm run build

# Start production server
npm run start

Deployment Options​

1. Cloudflare Pages (Primary)​

npm run deploy

Deploys to Cloudflare Pages using Wrangler.

Configuration: wrangler.toml (implicit)

2. Docker​

# Build image
docker build -t photobooth-web-app .

# Run container
docker run -p 3000:3000 photobooth-web-app

Environment Variables​

VariablePurposeDefault
NODE_ENVEnvironmentdevelopment
NEXT_PUBLIC_E2EE2E test modefalse

Future Considerations (ADRs)​

ADR-001: QR Code Download Feature​

Status: Proposed

Decision: Use Supabase for cloud image storage with QR code download.

Trade-offs:

  • βœ… Excellent DX, rapid implementation, free tier
  • ⚠️ Egress cost risk (2GB/month limit on free tier)
  • βœ… Scalable architecture path available

Alternative: Cloudflare R2 (no egress fees) - Re-evaluate if traffic exceeds free tier

ADR-002: Vendor Access Control​

Status: Proposed

Decision: Remote validation server with Cloudflare Workers + D1.

Implementation:

  • License key generation and validation
  • JWT token for offline verification
  • D1 database for license storage

Trade-offs:

  • βœ… Robust security, centralized management, scalable
  • ⚠️ Internet required for initial activation
  • ⚠️ Requires separate admin UI development

Known Limitations & Notes​

  1. Browser Support: Requires Web Bluetooth API support (Chrome, Edge, Opera)
  2. Camera: Requires HTTPS or localhost
  3. Print Size: Currently hardcoded to 30x50mm (Niimbot default)
  4. Image Count: Maximum 4 images per session
  5. PIN Default: Change default PIN (1234) in production
  6. Voucher: Disable in production if not used

API Reference (Internal)​

Settings Schema​

interface CustomizationSettings {
pageSettings: {
background: {
type: "Gradien" | "Warna" | "Gambar";
color?: string;
gradientStart?: string;
gradientEnd?: string;
};
title: { text: string; font: string; size: string; color: string };
subtitle: { text: string; font: string; size: string; color: string };
};
frameSettings: {
header: {
active: boolean;
text: string;
textColor: string;
fontSize: string;
fontFamily: string;
textAlign: string;
bold: boolean;
italic: boolean;
underline: boolean;
height: number;
};
footer: {
active: boolean;
text: string;
textColor: string;
fontSize: string;
fontFamily: string;
textAlign: string;
bold: boolean;
italic: boolean;
underline: boolean;
height: number;
};
background: { type: string; colorStart: string; colorEnd: string };
photoFrame: {
active: boolean;
thickness: number;
style: string;
color: string;
horizontalMargin: number;
};
};
button: ButtonSettings;
security: SecuritySettings;
printer: PrinterSettings;
voucher: VoucherSettings;
}

Quick Reference for New Engineers​

Starting Development​

  1. Clone repo β†’ npm install β†’ npm run dev
  2. Open http://localhost:3000
  3. Default PIN: 1234 β†’ Access /customization

Key Files to Modify​

TaskFile(s)
Add new framesrc/data/frames.json
Change defaultssrc/providers/SettingsProvider.tsx
Modify print logicsrc/providers/PrinterProvider.tsx
Image processingsrc/lib/image-processing.ts
Add pageCreate in src/pages/
Stylingsrc/app/globals.css, tailwind.config.ts

Debugging Tips​

  1. Bluetooth issues: Check browser compatibility (Chrome required)
  2. Camera issues: Ensure HTTPS or localhost
  3. Storage issues: Check browser storage quota
  4. E2E tests: Use NEXT_PUBLIC_E2E=true for mock printing

End of Technical Documentation