BSS UXI Design Specification - Attendant & Rider Apps¶
Document Purpose: Comprehensive UXI design specification for BSS Attendant and Rider mobile applications, implementing "Workflow as an integral act" with QR code interactions and form-based user experience.
Reference Workflows:
- Attendant Workflow: bss-attendant-workflow-enhanced.md
- Rider Workflow: bss-rider-workflow.md
Design Philosophy: - Single Form Continuity: One persistent form that evolves through the workflow - QR-Driven Interactions: QR codes as primary interaction mechanism - Real-Time State Visibility: Transparent system state and progress feedback - Mobile-First Design: Touch-optimized, thumb-friendly layouts
Design Principles¶
1. Workflow as Integral Act¶
The entire service transaction is presented as a single, coherent journey rather than disconnected screens.
Implementation: - Single persistent form that expands and updates through workflow stages - Progressive disclosure: Information appears as it becomes relevant - Contextual actions: Buttons change based on workflow state - Visual continuity: Smooth transitions without jarring screen changes - Clear progress indicators showing current position in workflow
2. QR-Driven Interaction Model¶
QR codes serve as primary interaction mechanism for identity verification, payment, and service coordination.
Implementation: - Camera-first design with QR scanner ready on app launch - Different QR purposes clearly distinguished (customer ID, payment, asset) - Fallback options: Manual entry when QR scanning fails - Dynamic QR code generation for payments - Multi-format support: QR codes, barcodes, NFC tags
3. Real-Time State Transparency¶
Users see system state, progress, and changes in real-time.
Implementation: - MQTT-driven real-time status updates - Live progress indicators on multi-step processes - State history audit trail in expandable sections - Errors shown inline with recovery suggestions - Skeleton screens during async operations
4. Mobile-First Touch Design¶
Optimized for one-handed mobile operation in field conditions.
Implementation: - Critical actions within thumb reach zone - Large touch targets: Minimum 44x44pt (Apple HIG) - Swipe gestures for secondary actions - Minimal text entry: QR scanning and selection over typing - High contrast for bright outdoor conditions
Attendant App Design¶
Screen 1: Home / Ready State¶
Purpose: Dashboard when no active service transaction
Layout Hierarchy:
Header (64px)
├── Station Name & Location
└── Battery Availability Count
Main Content Area
├── Primary CTA: Scan Customer QR (280px tall, centered)
│ ├── Camera Icon (80px)
│ └── Label "Scan Customer QR"
├── Secondary Actions (48px tall each)
│ ├── Manual Entry Button
│ └── View History Button
└── Current Queue Section
├── Section Header "Current Queue (2)"
└── Queue Items (80px each)
├── Customer Name & Status
└── Progress Indicator
Bottom Navigation (60px)
├── Settings Icon
├── Reports Icon
└── Alerts Icon
Key Measurements: - Screen Width: 375px (iPhone standard) - Header Height: 64px - Primary Button: 280px × 180px - Secondary Buttons: Full width × 48px - Queue Items: Full width × 80px - Bottom Nav: Full width × 60px
Color Scheme (Following DIRAC Color System): - Primary Action: Light Cyan (#E0F7FA) - DIRAC System Color - Success States: Light Green (#C8E6C9) - Internal Actor Color - Warning States: Gold (#FFD700) - External System Color - Background: White (#FFFFFF) - Text Primary: Dark Gray (#212121) - Text Secondary: Medium Gray (#757575)
Typography: - Header: SF Pro Display Bold, 20px - Section Titles: SF Pro Text Semibold, 16px - Body Text: SF Pro Text Regular, 14px - Button Labels: SF Pro Text Medium, 16px
User Interactions: - Tap "Scan Customer QR" → Opens camera with QR scanner overlay - Tap Manual Entry → Shows customer ID input dialog - Tap Queue Item → View transaction details (read-only) - Swipe Down → Refresh station status
Screen 2: Service Event Form - Progressive States¶
State 1: Customer Verified (After QR Scan)¶
Layout:
Progress Bar (8px)
└── Step 2 of 6 (33% complete)
Scrollable Content
├── Customer Information Card (Collapsible)
│ ├── Avatar & Name
│ ├── Customer ID
│ ├── Phone Number
│ └── Verification Badge
│
├── Subscription Status Card (Collapsible)
│ ├── Plan Name
│ ├── Period (Start - End, Days Remaining)
│ └── Status Badge (ACTIVE/EXPIRED)
│
├── Quota Status Card (Collapsible)
│ ├── Swap-Count Progress Bar
│ │ └── "6 / 10 used (4 remaining)"
│ ├── Electricity Progress Bar
│ │ └── "344 / 400 kWh (56 kWh left)"
│ └── Payment Status Badge
│
├── Asset Return Section (Active)
│ ├── Expected Battery ID
│ ├── Scan Button (Primary, Full Width, 120px)
│ └── Alternative: "First Visit" Button (Secondary)
│
└── Future Steps (Grayed Out)
├── Asset Issuance (Step 4)
├── Payment (Step 5)
└── Service Complete (Step 6)
Bottom Action Bar (Fixed, 80px)
└── Cancel Service Button
Card Specifications: - Card Padding: 16px - Card Margin: 12px horizontal, 8px vertical - Card Border Radius: 12px - Card Shadow: 0 2px 4px rgba(0,0,0,0.1)
Progress Bar: - Height: 8px - Background: Light Gray (#E0E0E0) - Fill: Light Cyan (#00BCD4) - Animation: Smooth transition on step change
Collapsible Card Behavior: - Tap header to expand/collapse - Chevron icon rotates 180° on state change - Smooth height animation (300ms ease-in-out) - Collapsed height: 60px (shows summary only) - Expanded height: Variable based on content
State 2: Battery Scanned (Asset Return Verified)¶
Changes from State 1:
Asset Return Section (Now Completed) ✅
├── Scanned Battery: BAT-12345 (matches expected)
├── Ownership: ✅ Verified
├── Condition: ✅ Acceptable
├── Incoming Electricity: 15% SOC (4.8 kWh)
│ ├── Battery icon with percentage
│ └── kWh value below
└── Auto-collapse after 2 seconds
Asset Issuance Section (Now Active)
├── Instruction: "Scan Outgoing Battery"
├── Scan Button (Primary, Full Width, 120px)
└── Available Battery Count: "12 batteries available"
Visual Feedback on Scan Success: - Green checkmark animation (500ms) - Haptic feedback (medium impact) - Success sound (optional, can be disabled) - Section border briefly pulses green - Auto-scroll to next active section
State 3: Outgoing Battery Scanned¶
Changes:
Asset Issuance Section (Completed) ✅
├── Outgoing Battery: BAT-67890
├── Outgoing Electricity: 95% SOC (30.4 kWh)
├── Net Electricity Delivered: 80% (25.6 kWh)
│ ├── Calculation shown clearly
│ └── Visual: 95% - 15% = 80% with battery icons
└── Auto-collapse after 2 seconds
Quota Validation Section (Auto-calculated, Read-only)
├── Swap-Count Impact
│ └── "6 → 7 used (3 remaining) ✅"
├── Electricity Impact
│ └── "344 → 369.6 kWh used (30.4 kWh left) ✅"
└── Status: ✅ Sufficient Quotas
OR
❌ Quota Exhausted - Top-up Required
Payment Section (Conditionally Active)
├── IF quotas sufficient: "No Payment Required"
├── IF quota exhausted: Payment QR Code & Amount
Quota Exhaustion Flow:
Payment Section (Active when quota exhausted)
├── Top-up Amount Breakdown
│ ├── Swap-Count Deficit: "1 swap needed"
│ ├── Electricity Deficit: "30 kWh needed"
│ └── Total Amount Due: "$15.00" (Large, bold)
│
├── Payment QR Code (300px × 300px, centered)
│ ├── Generated dynamically
│ └── Odoo payment link encoded
│
├── Payment Instructions
│ └── "Ask rider to scan QR with their phone"
│
└── Payment Status Indicator
├── Initial: ⏳ "Awaiting Payment..."
├── Success: ✅ "Payment Received"
└── Auto-refresh every 3 seconds
Payment QR Code Specifications: - Size: 300px × 300px - Margin: 24px all sides - Background: White - Foreground: Black - Error Correction: Level H (30%) - Encoding: Odoo payment URL with parameters: - merchant_id - amount - service_description - customer_id - transaction_id
State 4: Payment Completed (or Not Required)¶
Changes:
Payment Section (Completed) ✅
├── Payment Status: ✅ Paid
├── Receipt ID: PAY-78910
├── Amount: $15.00
└── Timestamp
Service Complete Section (Now Active)
├── Summary of Transaction
│ ├── Customer: John Doe
│ ├── Incoming Battery: BAT-12345 (15% → Returned)
│ ├── Outgoing Battery: BAT-67890 (95% → Issued)
│ ├── Net Electricity: 25.6 kWh delivered
│ ├── Updated Quotas
│ │ ├── Swaps: 7/10
│ │ └── Electricity: 369.6/400 kWh
│ └── Payment: $15.00 (if applicable)
│
└── Confirm Button (Primary, Full Width, 60px)
└── "Complete Service & Hand Over Battery"
Complete Service Button: - Height: 60px - Background: Light Green (#4CAF50) - Text: White, Bold, 18px - Haptic feedback on tap - Disabled until all validations pass - Loading spinner during MQTT publish
Screen 3: Receipt & Confirmation¶
Purpose: Transaction summary and digital receipt
Layout:
Header (80px)
├── Success Icon (48px checkmark in circle)
└── "Service Complete!"
Receipt Content (Scrollable)
├── Transaction ID
├── Timestamp
├── Customer Information
├── Service Details
│ ├── Battery Swap
│ │ ├── Returned: BAT-12345 (15% SOC)
│ │ └── Issued: BAT-67890 (95% SOC)
│ └── Electricity Delivered: 25.6 kWh
├── Quota Status (Updated)
│ ├── Swaps Remaining: 3
│ └── Electricity Remaining: 30.4 kWh
├── Payment Information (if applicable)
│ ├── Amount: $15.00
│ └── Receipt: PAY-78910
└── Next Service Date Estimate
Bottom Actions (Fixed, 120px)
├── Share Receipt Button (Secondary, 48px)
├── Print Receipt Button (Secondary, 48px)
└── New Service Button (Primary, 56px)
Receipt Styling: - Background: White card on light gray background - Card padding: 24px - Font: Monospace for IDs and numbers - Dividers between sections (1px, #E0E0E0) - Success color accents for positive values
Share/Print Actions: - Share: Opens native share sheet (email, SMS, WhatsApp) - Print: Sends to Bluetooth thermal printer if configured - Receipt format: Plain text with clear structure
Rider App Design¶
Screen 1: Service Discovery (W1)¶
Purpose: Find available swap stations and batteries based on service bundle
Layout:
Header (120px)
├── User Avatar & Name
├── Active Plan Badge
└── Quick Quota Summary
├── Swaps: 4 remaining
└── kWh: 56 remaining
Map View (Primary, 60% of screen)
├── User Location Pin (Blue)
├── Station Markers (Green with battery icon)
│ ├── Size indicates availability
│ └── Tap to select
├── Zoom Controls
└── Center on User Button
Station List (Bottom Sheet, Draggable)
├── Handle (Drag indicator, 4px × 32px)
├── Results Count: "3 stations nearby"
└── Station Cards (Scrollable)
├── Station Name & Distance
├── Available Batteries Count
├── Estimated Wait Time
├── Directions Button
└── Signal Intent Button (Primary)
Map Specifications: - Zoom level: Auto-adjust to show all stations within 10km - User location: Blue pulsing circle - Station markers: Custom icon (battery symbol) - Marker size: 32px-48px based on battery count - Selected marker: Highlighted with ring
Bottom Sheet Behavior: - Initial state: Collapsed (showing 2 cards) - Drag up: Expands to show full list - Drag down: Collapses to minimum - Snap points: 30%, 60%, 90% of screen height - Dismiss: Swipe down past 30%
Station Card:
Station Card (Full Width, 120px)
├── Station Name (Bold, 16px)
├── Distance & Direction
│ └── "2.3 km • Northwest"
├── Battery Availability
│ ├── Icon count (battery icons × available count)
│ └── "12 batteries • No wait"
├── Quick Actions
├── Directions (Icon button, opens maps app)
└── Signal Intent (Primary, 40px tall)
Color Coding for Availability: - High availability (8+): Green - Medium availability (4-7): Yellow - Low availability (1-3): Orange - No availability: Red (grayed out, no intent button)
Screen 2: Service Intent Signal (W2)¶
Trigger: Rider taps "Signal Intent" on a station card
Layout (Modal overlay):
Modal (Rounded corners, slides up)
├── Header
│ ├── Station Name
│ └── Close Button (×)
│
├── Confirmation Message
│ └── "Signal your arrival to Nairobi Hub 01?"
│
├── Your Service Requirements (Read-only)
│ ├── Service: Battery Swap
│ ├── Current Battery: BAT-12345
│ └── Estimated Service Time: 5-10 minutes
│
├── Estimated Arrival Time (Editable)
│ ├── Picker: "15 minutes" (default)
│ └── Options: 5, 10, 15, 20, 30, 45, 60 minutes
│
└── Action Buttons (Stacked, Full Width)
├── Confirm & Navigate (Primary, 56px)
│ └── "Send Signal & Get Directions"
└── Cancel (Secondary, 48px)
After Confirmation:
Success State (Replaces modal content)
├── Success Icon (Animated checkmark)
├── Message: "Station notified of your arrival"
├── Status Updates (Real-time)
│ ├── ✅ Signal sent
│ ├── ✅ Station acknowledged
│ └── ⏳ Attendant preparing...
└── Navigate Button (Primary)
└── Opens navigation app with destination
Modal Specifications: - Width: 90% of screen width - Max width: 400px - Border radius: 20px (top corners) - Background: White - Backdrop: Semi-transparent dark (#000000, 50% opacity) - Animation: Slide up from bottom (300ms)
Screen 3: At Station - Check-in (W3)¶
Purpose: Location authentication and customer-location binding
Layout:
Header (Contextual)
├── Station Name
├── Status: "You're here!"
└── Distance: "Within 50m"
Main Content
├── Check-in Prompt
│ └── "Show your QR code to the attendant"
│
├── Customer QR Code (Large, 320px × 320px)
│ ├── Customer ID encoded
│ ├── ServicePlan ID encoded
│ └── Session token encoded
│
├── QR Code Label
│ └── Customer ID: CUST-12345
│
└── Alternative Check-in Methods
├── NFC Tap (if supported)
│ └── "Or tap your phone to the station reader"
└── Manual Code Entry
└── "Code: 1234-5678"
Status Panel (Below QR, Collapsible)
├── Your Service Details
│ ├── Plan: Weekly Freedom Basic
│ ├── Swaps Left: 4
│ ├── kWh Left: 56
│ └── Current Battery: BAT-12345
└── Station Status
├── Queue Position: 2nd in line
└── Estimated Wait: 8 minutes
QR Code Specifications: - Size: 320px × 320px - Margin: 32px all sides - Background: White card with shadow - Brightness: Auto-boost to maximum - Timeout: Regenerates every 60 seconds for security - Encoding Format:
{
"customer_id": "CUST-12345",
"plan_id": "bss-plan-weekly-freedom-nairobi-v2-plan1",
"session_token": "encrypted_session_token",
"timestamp": "2025-11-18T10:30:00Z",
"station_id": "nairobi-hub-01"
}
Auto-brightness: - Screen brightness forced to 100% on this screen - Prevents screen dimming while QR displayed - Restores previous brightness on exit
Screen 4: Service in Progress (W4 & W5)¶
Purpose: Real-time service progress tracking
Layout:
Progress Timeline (Vertical, left-aligned)
├── 1. Check-in ✅ Completed (10:25 AM)
├── 2. Battery Return ⏳ In Progress
│ ├── "Attendant scanning your battery..."
│ └── Spinner animation
├── 3. Battery Issuance ⏺ Pending
├── 4. Payment (if needed) ⏺ Pending
└── 5. Complete ⏺ Pending
Current Step Details (Card)
├── Step Title: "Battery Return"
├── Description
│ └── "Attendant is verifying your battery"
├── What's Happening (Real-time updates)
│ ├── ✅ Battery ID verified: BAT-12345
│ ├── ✅ Ownership confirmed
│ ├── ⏳ Checking battery condition...
│ └── Last updated: 2 seconds ago
└── Estimated Time Remaining: "2-3 minutes"
Bottom Actions
└── Need Help? Button (Secondary)
└── Opens help options (call, message)
Timeline Styling: - Line thickness: 4px - Line color: Light gray (#E0E0E0) - Active step: Light cyan (#00BCD4) - Completed step: Light green (#4CAF50) - Step circle: 32px diameter - Icon size: 20px inside circle
Real-Time Updates: - MQTT subscription to service event topics - Updates appear as they occur (no manual refresh) - Smooth animation when new updates arrive - "Last updated" timestamp for transparency
Payment Step (When Triggered):
Payment Step (Active)
├── Payment Required Notice
│ ├── Reason: "Quota Top-up Required"
│ └── Amount: $15.00
│
├── Payment Breakdown
│ ├── Swap-Count Top-up: $10.00
│ └── Electricity Top-up: $5.00
│
├── Payment QR Code (Generated by attendant)
│ └── "Scan the QR code shown by attendant"
│
└── OR Manual Payment Options
├── M-Pesa Button
├── Card Payment Button
└── Other Payment Methods
Payment Flow: 1. System detects quota exhaustion 2. Rider notified: "Top-up required: $15.00" 3. Attendant generates payment QR 4. Rider scans QR with phone 5. Opens Odoo payment page 6. Rider completes payment 7. Confirmation → Service resumes
Screen 5: Service Complete¶
Purpose: Transaction confirmation and receipt
Layout:
Success Animation (Top, 200px)
├── Animated checkmark (2s animation)
└── "Service Complete!"
Transaction Summary Card
├── Service Type: Battery Swap
├── Timestamp: 10:35 AM
├── Duration: 10 minutes
│
├── Battery Exchange
│ ├── Returned: BAT-12345 (15% → 4.8 kWh)
│ └── Received: BAT-67890 (95% → 30.4 kWh)
│
├── Energy Received: 25.6 kWh
│
├── Payment (if applicable)
│ ├── Amount Paid: $15.00
│ └── Receipt: PAY-78910
│
└── Updated Quotas
├── Swaps Remaining: 3 / 10
│ └── Progress bar (70% consumed)
├── Electricity Remaining: 30.4 / 400 kWh
└── Progress bar (92% consumed)
Important Information (Warning card, if applicable)
└── "Low on quotas! 30.4 kWh remaining"
└── Suggestion: "Consider topping up"
Bottom Actions
├── Save Receipt Button (Secondary, 48px)
├── Rate Experience Button (Secondary, 48px)
└── Done Button (Primary, 56px)
└── Returns to home screen
Receipt Export Options: - Email receipt - Download PDF - Share via SMS/WhatsApp - Add to digital wallet (Apple Wallet, Google Pay)
Shared Design Components¶
1. Battery Level Indicator¶
Visual Component (Reusable across both apps):
Battery Icon Component
├── Outline: Battery shape (40px × 20px)
├── Fill: Colored bar based on percentage
│ ├── 80-100%: Green
│ ├── 40-79%: Yellow
│ ├── 10-39%: Orange
│ └── 0-9%: Red
├── Percentage Label
│ └── "95%" (Below or beside icon)
└── kWh Value (Optional)
└── "30.4 kWh" (Smaller text)
Usage: - Asset return/issuance sections - Transaction summaries - Battery fleet displays
2. Progress Bar¶
Specifications:
Progress Bar Component
├── Height: 8px (default) or 12px (large)
├── Background: Light gray (#E0E0E0)
├── Fill: Dynamic color based on context
│ ├── Quota usage: Light cyan (#00BCD4)
│ ├── Success: Green (#4CAF50)
│ └── Warning: Orange (#FF9800)
├── Border Radius: 4px
├── Animation: Smooth fill transition (500ms)
└── Label (Optional)
├── Above: "6 / 10 used"
└── Below: "(4 remaining)"
3. Status Badge¶
Component:
Status Badge
├── Size: 24px height
├── Padding: 8px horizontal, 4px vertical
├── Border Radius: 12px (pill shape)
├── Font: Bold, 12px, Uppercase
└── Color Variants:
├── ACTIVE: Green background, white text
├── PENDING: Yellow background, dark text
├── EXPIRED: Red background, white text
├── COMPLETE: Blue background, white text
└── SUSPENDED: Gray background, white text
4. Collapsible Card¶
Behavior Specification:
Collapsible Card Component
├── Header (Always visible, 60px)
│ ├── Title (Left-aligned)
│ ├── Summary (Below title, smaller)
│ └── Chevron Icon (Right-aligned, rotates on toggle)
│
├── Content (Expandable, variable height)
│ ├── Padding: 16px
│ └── Content elements
│
└── Interaction
├── Tap header to toggle
├── Smooth height animation (300ms ease-in-out)
├── Haptic feedback on toggle
└── State persists during session
Animation Curve: - Easing: Cubic-bezier(0.4, 0.0, 0.2, 1) - Duration: 300ms - Property: Max-height (for smooth collapse)
5. Action Button Variants¶
Primary Button:
Specifications:
├── Height: 56px (default) or 48px (compact)
├── Width: Full width or auto (with min-width: 160px)
├── Background: Light Cyan (#00BCD4) or Green (#4CAF50)
├── Text: White, Bold, 16px
├── Border Radius: 8px
├── Shadow: 0 4px 8px rgba(0,0,0,0.15)
├── Active State: Darken 10%, scale 0.98
└── Disabled State: Gray (#BDBDBD), no shadow
Secondary Button:
Specifications:
├── Height: 48px
├── Background: Transparent or Light Gray (#F5F5F5)
├── Border: 2px solid Light Gray (#E0E0E0)
├── Text: Dark Gray (#424242), Medium, 14px
├── Border Radius: 8px
├── Active State: Background Light Gray (#EEEEEE)
└── Disabled State: Faded (opacity: 0.5)
Icon Button:
Specifications:
├── Size: 48px × 48px (touch target)
├── Icon Size: 24px
├── Background: Transparent or subtle color
├── Border Radius: 24px (circular)
├── Active State: Background tint
└── Ripple Effect: Material Design ripple
6. QR Code Scanner Overlay¶
Full-Screen Component:
Scanner Overlay
├── Camera View (Full screen)
├── Viewfinder Frame (280px × 280px, centered)
│ ├── Corner brackets (white, 4px thick)
│ ├── Scanning line (animated, moves up/down)
│ └── Semi-transparent mask around frame
├── Instructions (Top, 80px from top)
│ └── "Align QR code within frame"
├── Torch Toggle (Top right, icon button)
├── Close Button (Top left, × icon)
└── Manual Entry Option (Bottom, 80px from bottom)
Scanner Animation: - Scanning line: 2px height, white with glow - Movement: Top to bottom, 2s loop - Glow effect: Box-shadow with light cyan
Success Feedback: - Green flash over frame (200ms) - Haptic feedback (success pattern) - Success sound (if enabled) - Auto-dismiss scanner after 500ms
QR Code Integration Patterns¶
QR Code Types & Encoding¶
1. Customer Identity QR¶
Used by: Rider app (displayed), Attendant app (scanned)
Encoded Data:
{
"type": "customer_identity",
"customer_id": "CUST-12345",
"plan_id": "bss-plan-weekly-freedom-nairobi-v2-plan1",
"session_token": "enc_token_xyz789",
"timestamp": "2025-11-18T10:30:00Z",
"expires": "2025-11-18T10:31:00Z",
"checksum": "abc123def456"
}
Security: - Regenerates every 60 seconds - Session token encrypted with AES-256 - Timestamp validation (±2 minutes tolerance) - Checksum prevents tampering
2. Payment QR¶
Used by: Attendant app (generated), Rider app/phone (scanned)
Encoded Data:
https://payments.odoo.oves.com/pay?
merchant=nairobi-hub-01
&amount=15.00
¤cy=USD
&service=battery-swap-topup
&customer=CUST-12345
&transaction=TXN-789456
&signature=sig_xyz789abc
Specifications: - Format: URL with query parameters - Opens: Odoo payment page in default browser - Signature: HMAC-SHA256 for verification - Expiration: 10 minutes
3. Battery Asset QR¶
Used by: Attendant app (scanned)
Encoded Data:
{
"type": "battery_asset",
"asset_id": "BAT-12345",
"fleet_id": "battery-fleet-kenya-premium",
"last_service": "2025-11-15T08:00:00Z",
"soc_percent": 15,
"health_status": "good"
}
Usage: - Scanned during asset return - Scanned during asset issuance - Validates against expected asset in ServicePlan
Form-Based UX Architecture¶
Single Persistent Form Pattern¶
Core Concept: The service transaction is represented as a single form that evolves through states rather than navigating between separate screens.
Implementation Strategy:
1. Form State Machine¶
Form States (Attendant App):
├── IDLE (Home screen)
├── CUSTOMER_SCANNED (Step 2)
├── ASSET_RETURN_ACTIVE (Step 3)
├── ASSET_RETURN_COMPLETE
├── ASSET_ISSUANCE_ACTIVE (Step 4)
├── ASSET_ISSUANCE_COMPLETE
├── PAYMENT_REQUIRED (Step 5, conditional)
├── PAYMENT_AWAITING
├── PAYMENT_COMPLETE
├── SERVICE_READY_TO_COMPLETE (Step 6)
└── SERVICE_COMPLETE (Receipt screen)
State Transitions: - Triggered by user actions (scanning, button taps) - Triggered by system events (MQTT messages, validations) - Each transition updates form sections visibility/content - Progress bar reflects current state
2. Section Visibility Rules¶
Visibility Matrix:
State | Customer | Subscription | Quota | Asset Return | Asset Issue | Payment | Complete
-------------------------+----------+--------------+-------+--------------+-------------+---------+---------
CUSTOMER_SCANNED | Visible | Visible | Visible| Active | Hidden | Hidden | Hidden
ASSET_RETURN_COMPLETE | Collapsed| Collapsed | Collapsed| Completed | Active | Hidden | Hidden
ASSET_ISSUANCE_COMPLETE | Collapsed| Collapsed | Updated| Completed | Completed | Active | Hidden
PAYMENT_COMPLETE | Collapsed| Collapsed | Updated| Collapsed | Collapsed | Complete| Active
Behavior: - Visible: Section displayed in expanded state - Collapsed: Section displayed in collapsed state (summary only) - Active: Section is current focus, highlighted, interactive - Completed: Section shows checkmark, auto-collapses after 2s - Hidden: Section not rendered in DOM - Updated: Section content refreshed with new data
3. Progressive Disclosure Mechanics¶
Information Reveal Strategy:
Initial Display (Minimal):
└── Show only immediately relevant information
User Progresses:
├── Completed sections collapse automatically
├── Next section expands and scrolls into view
├── Summary information moves to collapsed headers
└── Detailed info hidden but accessible via tap
Benefits:
├── Reduced cognitive load
├── Clear focus on current step
├── Easy access to previous information if needed
└── Mobile-friendly (less scrolling)
Example - Quota Status Section:
Collapsed View (60px):
┌─────────────────────────────────┐
│ 📊 Quotas: 4 swaps, 56 kWh [▼]│
└─────────────────────────────────┘
Expanded View (180px):
┌─────────────────────────────────┐
│ 📊 QUOTA STATUS [▲] │
├─────────────────────────────────┤
│ Swap-Count │
│ ████████░░ 6/10 (4 remaining) │
│ │
│ Electricity-Fuel │
│ ███████████░ 344/400 kWh │
│ (56 kWh remaining) │
│ │
│ Payment Status: CURRENT ✅ │
└─────────────────────────────────┘
State Management & Real-Time Updates¶
MQTT Integration for Live Updates¶
Subscription Topics (Attendant App):
Topics to Subscribe:
├── echo/abs/attendant/plan/{plan_id}/payment_received
├── echo/abs/attendant/plan/{plan_id}/quota_updated
├── echo/abs/attendant/station/{station_id}/queue_updated
└── emit/abs/bss/service/+/status_change
Event Handling:
// Pseudo-code for real-time form updates
mqtt.on('message', (topic, payload) => {
const event = JSON.parse(payload);
switch (event.type) {
case 'payment_received':
updateFormSection('payment', {
status: 'complete',
receipt_id: event.receipt_id
});
transitionFormState('PAYMENT_COMPLETE');
break;
case 'quota_updated':
updateFormSection('quota', {
swaps_remaining: event.swaps_remaining,
kwh_remaining: event.kwh_remaining
});
break;
case 'queue_updated':
updateQueueDisplay(event.queue_data);
break;
}
});
Update Animation: - Changed values briefly highlight (yellow background fade) - Duration: 1s fade out - Smooth number transitions (count-up animation) - Status badges transition with scale effect
Local State Management¶
State Structure:
interface AttendantFormState {
currentStep: number; // 1-6
formState: FormStateEnum;
customer: {
id: string;
name: string;
phone: string;
verified: boolean;
};
subscription: {
plan_id: string;
plan_name: string;
start_date: Date;
end_date: Date;
status: 'ACTIVE' | 'EXPIRED' | 'SUSPENDED';
};
quotas: {
swap_count: { used: number; limit: number; };
electricity: { used: number; limit: number; };
payment_status: 'CURRENT' | 'OVERDUE';
};
assetReturn: {
expected_battery_id: string;
scanned_battery_id: string | null;
incoming_soc: number | null;
ownership_verified: boolean;
condition: 'good' | 'damaged' | null;
};
assetIssuance: {
outgoing_battery_id: string | null;
outgoing_soc: number | null;
net_electricity_delivered: number | null;
};
payment: {
required: boolean;
amount: number;
qr_code_data: string | null;
status: 'pending' | 'awaiting' | 'complete' | null;
receipt_id: string | null;
};
validation: {
can_proceed: boolean;
blocking_issues: string[];
};
}
State Persistence: - Local storage backup every state change - Restore on app crash/restart - Clear on successful completion - Retain for 24 hours in case of issues
Error Handling & User Feedback¶
Error Categories & Responses¶
1. Scan Errors¶
QR Scan Failures:
Error Display (Toast notification):
┌─────────────────────────────────┐
│ ⚠️ QR Code Not Recognized │
│ Try again or use Manual Entry │
│ [Retry] [Manual Entry] │
└─────────────────────────────────┘
Duration: 5 seconds or until dismissed
Position: Bottom of screen, above actions
Battery Mismatch:
Error Display (Inline, in Asset Return section):
┌─────────────────────────────────┐
│ ❌ Battery Mismatch │
│ │
│ Expected: BAT-12345 │
│ Scanned: BAT-99999 │
│ │
│ Please scan the correct battery │
│ [Try Again] │
└─────────────────────────────────┘
Haptic: Error pattern
Sound: Error beep (if enabled)
2. Quota Exhaustion¶
Graceful Handling:
Quota Exhaustion Display:
┌─────────────────────────────────┐
│ ⚠️ Quota Exhausted │
│ │
│ Swap-Count: 10/10 (0 left) │
│ Top-up required: $10.00 │
│ │
│ Electricity: 400/400 kWh (0) │
│ Top-up required: $5.00 │
│ │
│ Total Payment: $15.00 │
│ │
│ [Generate Payment QR] │
└─────────────────────────────────┘
User Guidance: - Clear explanation of issue - Specific quota deficits shown - Calculated top-up amount - Direct action to resolve (payment)
3. Payment Timeout¶
Timeout Handling:
After 5 minutes of awaiting payment:
┌─────────────────────────────────┐
│ ⏰ Payment Timeout │
│ │
│ Payment not received within │
│ expected time (5 minutes) │
│ │
│ Options: │
│ • [Refresh Status] Check again │
│ • [Generate New QR] New code │
│ • [Cancel Service] Abort swap │
└─────────────────────────────────┘
4. Network Errors¶
Offline State:
Banner (Fixed at top):
┌─────────────────────────────────┐
│ 📡 Offline Mode │
│ Changes will sync when online │
└─────────────────────────────────┘
Behavior:
├── Form remains functional
├── Actions queued locally
├── Syncs when connection restored
└── Conflict resolution UI if needed
Connection Restored:
Toast (Auto-dismiss after 3s):
┌─────────────────────────────────┐
│ ✅ Back Online │
│ Syncing changes... │
└─────────────────────────────────┘
Validation Feedback¶
Real-Time Validation:
Field-Level Validation (Example: Manual Entry):
┌─────────────────────────────────┐
│ Customer ID │
│ [CUST-_____] │
│ │
│ ❌ ID must be 5 digits │
└─────────────────────────────────┘
Valid State:
┌─────────────────────────────────┐
│ Customer ID │
│ [CUST-12345] ✅ │
└─────────────────────────────────┘
Form-Level Validation:
"Complete Service" button states:
Disabled (issues present):
┌─────────────────────────────────┐
│ [Complete Service - Disabled] │
│ │
│ Cannot proceed: │
│ • Payment not received │
│ • Battery not handed over │
└─────────────────────────────────┘
Enabled (ready):
┌─────────────────────────────────┐
│ [✅ Complete Service] │
│ All validations passed │
└─────────────────────────────────┘
Accessibility & Internationalization¶
Accessibility Standards¶
WCAG 2.1 Level AA Compliance:
Color Contrast: - Text on background: Minimum 4.5:1 ratio - Large text (18px+): Minimum 3:1 ratio - Interactive elements: Clear focus indicators - Status not conveyed by color alone (icons + text)
Touch Targets: - Minimum size: 44px × 44px (Apple HIG) - Spacing between targets: 8px minimum - Larger targets for primary actions: 56px height
Screen Reader Support:
ARIA Labels (Examples):
├── "Scan customer QR code button"
├── "Customer John Doe, verified"
├── "Quota status: 4 swaps remaining out of 10"
├── "Payment required: 15 dollars"
└── "Service complete button, enabled"
Keyboard Navigation (For tablet/web versions): - Logical tab order following visual flow - Enter/Space activates buttons - Esc dismisses modals - Arrow keys navigate lists
Dynamic Type Support: - Text scales with system font size settings - Layout adapts to larger text (iOS Dynamic Type, Android) - Minimum font size: 14px (before scaling)
Internationalization¶
Supported Languages (Initial): - English (en-US) - Swahili (sw-KE) - Primary for Kenya market - French (fr-FR) - For potential expansion
i18n Implementation:
// String resources structure
{
"en-US": {
"attendant.home.scan_button": "Scan Customer QR",
"attendant.form.quota_status": "Quota Status",
"attendant.form.swaps_remaining": "{remaining} swaps remaining",
"attendant.payment.amount_due": "Amount Due: ${amount}",
"attendant.complete.success": "Service Complete!"
},
"sw-KE": {
"attendant.home.scan_button": "Changanua QR ya Mteja",
"attendant.form.quota_status": "Hali ya Mgao",
"attendant.form.swaps_remaining": "Mabadiliko {remaining} yaliyobaki",
"attendant.payment.amount_due": "Kiasi Kinachohitajika: ${amount}",
"attendant.complete.success": "Huduma Imekamilika!"
}
}
Number & Currency Formatting: - Currency: Locale-aware (USD, KES, etc.) - Numbers: Locale-specific thousand separators - Dates: Locale-specific formats - Time: 12/24-hour based on locale
RTL Support (Future): - Layout mirroring for RTL languages - Icon directionality adjustments - Text alignment changes
Technical Implementation Specifications¶
Technology Stack¶
Frontend Framework: - React Native (mobile apps) - React version: 18.x - TypeScript for type safety
State Management: - React Context API for global state - React hooks (useState, useEffect, useReducer) - Local storage persistence: AsyncStorage (React Native)
MQTT Client: - Library: mqtt.js or paho-mqtt - WebSocket over TLS (wss://) - Auto-reconnection with exponential backoff
QR Code: - Generation: react-native-qrcode-svg - Scanning: react-native-camera or expo-camera - Barcode support: Multiple formats (QR, Code128, etc.)
Navigation: - React Navigation 6.x - Stack navigator for screen flow - Modal presentation for overlays
Component Architecture¶
Folder Structure:
/src
/components
/shared
- Button.tsx
- Card.tsx
- ProgressBar.tsx
- StatusBadge.tsx
- BatteryIcon.tsx
- QRScanner.tsx
/attendant
- ServiceEventForm.tsx
- AssetReturnSection.tsx
- AssetIssuanceSection.tsx
- PaymentSection.tsx
- QuotaDisplay.tsx
/rider
- StationMap.tsx
- StationCard.tsx
- ServiceProgress.tsx
- CustomerQRCode.tsx
/screens
/attendant
- HomeScreen.tsx
- ServiceScreen.tsx
- ReceiptScreen.tsx
/rider
- DiscoveryScreen.tsx
- IntentScreen.tsx
- CheckInScreen.tsx
- ProgressScreen.tsx
- CompleteScreen.tsx
/services
- mqttService.ts
- qrService.ts
- locationService.ts
- apiService.ts
/store
- attendantContext.tsx
- riderContext.tsx
/utils
- formatters.ts
- validators.ts
- constants.ts
/types
- models.ts
- api.ts
API Integration Points¶
GraphQL Endpoints (via FED):
# Query customer and plan data
query GetCustomerServicePlan($customerId: ID!) {
customer(id: $customerId) {
id
name
phone
servicePlans {
id
templateId
subscriptionStart
subscriptionEnd
serviceStates {
serviceId
used
quota
}
paymentStatus
}
}
}
# Mutation for service completion
mutation CompleteServiceEvent($input: ServiceEventInput!) {
completeServiceEvent(input: $input) {
transactionId
updatedQuotas {
serviceId
used
quota
}
receipt {
id
amount
timestamp
}
}
}
MQTT Topics:
Attendant App Subscriptions:
├── echo/abs/attendant/plan/{plan_id}/payment_received
├── echo/abs/attendant/plan/{plan_id}/quota_updated
├── echo/abs/attendant/station/{station_id}/queue_updated
└── emit/odoo/payment/plan/{plan_id}/payment_confirmed
Rider App Subscriptions:
├── echo/abs/rider/plan/{plan_id}/service_progress
├── echo/abs/rider/plan/{plan_id}/payment_required
└── emit/abs/bss/service/{customer_id}/status_change
Performance Optimizations¶
Rendering: - React.memo for expensive components - useMemo for computed values - useCallback for event handlers - Virtualized lists for long queues/station lists
Images & Assets: - SVG icons (scalable, small file size) - WebP format for photos - Lazy loading for off-screen content - Image caching with react-native-fast-image
Network: - GraphQL query batching - MQTT message compression - Optimistic UI updates - Background sync for offline changes
Storage: - IndexedDB/AsyncStorage for local data - Cache ServicePlan data (1-hour TTL) - Queue failed API calls for retry - Clean up old cached data periodically
Next Steps: Implementation Roadmap¶
Phase 1: Core Attendant App (Weeks 1-3)¶
- ✅ Home screen with QR scanner
- ✅ Service Event Form structure
- ✅ Customer verification flow
- ✅ Asset return/issuance sections
- ✅ Basic MQTT integration
Phase 2: Payment & Completion (Weeks 4-5)¶
- ✅ Payment QR generation
- ✅ Payment status polling
- ✅ Service completion flow
- ✅ Receipt generation
Phase 3: Rider App Foundation (Weeks 6-8)¶
- ✅ Service discovery with map
- ✅ Station list and filtering
- ✅ Intent signaling flow
- ✅ Customer QR code display
Phase 4: Rider Service Tracking (Weeks 9-10)¶
- ✅ Check-in screen
- ✅ Service progress tracking
- ✅ Payment flow (rider side)
- ✅ Completion and receipt
Phase 5: Polish & Testing (Weeks 11-12)¶
- ✅ Error handling refinement
- ✅ Offline mode testing
- ✅ Accessibility audit
- ✅ Performance optimization
- ✅ User acceptance testing
Design Review Checklist¶
- ✅ Workflow Integration: Designs map to finalized workflow documents
- ✅ QR Code Patterns: All QR interactions clearly specified
- ✅ Form Continuity: Single persistent form pattern implemented
- ✅ Real-Time Updates: MQTT integration points defined
- ✅ Mobile Optimization: Touch targets, thumb zones, gestures
- ✅ Error Handling: Comprehensive error states and recovery
- ✅ Accessibility: WCAG 2.1 AA compliance planned
- ✅ Internationalization: i18n structure defined
- ✅ Component Reusability: Shared components identified
- ✅ State Management: Clear state structure and transitions
- ✅ Performance: Optimization strategies outlined
- ✅ DIRAC Alignment: Color system and component integration
Document Status: Ready for Figma Implementation
Last Updated: 2025-11-18
Version: 1.0
Approved for: UXI Design Phase