Files
projectmycelium/docs/dev/design/archive/vision/parts/roadmap/dual-ux-specification.md
2025-09-01 21:37:01 -04:00

18 KiB

Dual UX Specification: Modern App + E-commerce Flows

Document Purpose: Comprehensive UX specification supporting both modern app-style instant purchase and traditional e-commerce cart workflows.

Last Updated: 2025-08-04
Status: Implementation Ready


Overview

The Project Mycelium supports two distinct user experience patterns to accommodate different user preferences and use cases:

  1. Modern App Flow (OpenRouter-style): Instant purchase with wallet top-up
  2. Traditional E-commerce Flow: Cart-based shopping with checkout

UX Flow Comparison

Flow 1: Modern App Style (OpenRouter-inspired)

graph TD
    A[Browse Marketplace] --> B[Register/Login]
    B --> C[View Service]
    C --> D[Buy Now Button]
    D --> E{TFC Balance Check}
    E -->|Sufficient| F[Instant Deploy]
    E -->|Insufficient| G[Auto Top-up Modal]
    G --> H[Stripe Payment]
    H --> I[TFC Added]
    I --> F
    F --> J[Deployment Status]
    J --> K[Service Active]

Key Features:

  • Instant Purchase: Single-click buying
  • Auto Top-up: Seamless balance management
  • Wallet-centric: Balance always visible
  • Minimal Friction: No cart, no checkout process

Flow 2: Traditional E-commerce Style

graph TD
    A[Browse Marketplace] --> B[View Service]
    B --> C[Add to Cart]
    C --> D[Continue Shopping]
    D --> E[Review Cart]
    E --> F[Checkout]
    F --> G{Payment Method}
    G -->|TFC Balance| H[Pay with TFC]
    G -->|Credit Card| I[Stripe Checkout]
    I --> J[TFC Purchase]
    J --> H
    H --> K[Batch Deployment]
    K --> L[Order Confirmation]

Key Features:

  • Bulk Shopping: Multiple items in cart
  • Price Comparison: Review before purchase
  • Batch Deployment: Deploy multiple services together
  • Familiar UX: Traditional e-commerce experience

Detailed UX Specifications

Modern App Flow Implementation

1. Wallet-First Interface

<!-- Wallet Status Component (Always Visible) -->
<div class="wallet-status-bar">
    <div class="balance-display">
        <span class="balance-amount">{{user.tfc_balance}} TFC</span>
        <span class="usd-equivalent">${{user.tfc_balance}} USD</span>
    </div>
    <div class="wallet-actions">
        <button class="btn btn-sm btn-outline-primary" onclick="showTopUpModal()">
            <i class="bi bi-plus-circle"></i> Top Up
        </button>
        <button class="btn btn-sm btn-outline-secondary" onclick="toggleAutoTopUp()">
            <i class="bi bi-arrow-repeat"></i> Auto Top-up: {{#if user.auto_topup}}ON{{else}}OFF{{/if}}
        </button>
    </div>
</div>

2. Buy Now Button (Primary Action)

<!-- Service Card with Buy Now -->
<div class="service-card modern-style">
    <div class="service-header">
        <h4>{{service.name}}</h4>
        <div class="price-tag">
            <span class="tfc-price">{{service.price_tfc}} TFC</span>
            <span class="deployment-time">~2 min deploy</span>
        </div>
    </div>
    
    <div class="service-actions">
        <button class="btn btn-primary btn-lg buy-now-btn" 
                onclick="buyNowInstant('{{service.id}}')">
            <i class="bi bi-lightning-fill"></i>
            Buy Now & Deploy
        </button>
        
        <button class="btn btn-outline-secondary add-to-cart-btn"
                onclick="addToCart('{{service.id}}')">
            <i class="bi bi-cart-plus"></i>
            Add to Cart
        </button>
    </div>
    
    <!-- Balance Check Indicator -->
    <div class="balance-check">
        {{#if (gte user.tfc_balance service.price_tfc)}}
            <span class="text-success">
                <i class="bi bi-check-circle"></i> Ready to deploy
            </span>
        {{else}}
            <span class="text-warning">
                <i class="bi bi-exclamation-triangle"></i> 
                Need {{subtract service.price_tfc user.tfc_balance}} more TFC
            </span>
        {{/if}}
    </div>
</div>

3. Auto Top-up Configuration

<!-- Auto Top-up Settings Modal -->
<div class="modal fade" id="autoTopUpModal">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5>Auto Top-up Settings</h5>
            </div>
            <div class="modal-body">
                <div class="form-group">
                    <label>Enable Auto Top-up</label>
                    <div class="form-check form-switch">
                        <input class="form-check-input" type="checkbox" id="enableAutoTopUp">
                        <label class="form-check-label">Automatically add TFC when balance is low</label>
                    </div>
                </div>
                
                <div class="form-group">
                    <label>Trigger Threshold</label>
                    <select class="form-select" id="topUpThreshold">
                        <option value="10">When balance < 10 TFC</option>
                        <option value="25">When balance < 25 TFC</option>
                        <option value="50" selected>When balance < 50 TFC</option>
                        <option value="100">When balance < 100 TFC</option>
                    </select>
                </div>
                
                <div class="form-group">
                    <label>Top-up Amount</label>
                    <select class="form-select" id="topUpAmount">
                        <option value="50">Add 50 TFC ($50)</option>
                        <option value="100" selected>Add 100 TFC ($100)</option>
                        <option value="200">Add 200 TFC ($200)</option>
                        <option value="500">Add 500 TFC ($500)</option>
                    </select>
                </div>
                
                <div class="alert alert-info">
                    <i class="bi bi-info-circle"></i>
                    Auto top-up uses your saved payment method. You'll receive an email confirmation for each transaction.
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                <button type="button" class="btn btn-primary" onclick="saveAutoTopUpSettings()">Save Settings</button>
            </div>
        </div>
    </div>
</div>

Traditional E-commerce Flow Implementation

1. Shopping Cart Component

<!-- Shopping Cart (Sidebar or Page) -->
<div class="shopping-cart">
    <div class="cart-header">
        <h4>Shopping Cart</h4>
        <span class="item-count">{{cart.items.length}} items</span>
    </div>
    
    <div class="cart-items">
        {{#each cart.items}}
        <div class="cart-item">
            <div class="item-info">
                <h6>{{this.service_name}}</h6>
                <p class="item-specs">{{this.cpu}}CPU • {{this.memory}}GB RAM • {{this.storage}}GB</p>
            </div>
            <div class="item-price">
                <span class="tfc-price">{{this.price_tfc}} TFC</span>
                <button class="btn btn-sm btn-outline-danger" onclick="removeFromCart('{{this.id}}')">
                    <i class="bi bi-trash"></i>
                </button>
            </div>
        </div>
        {{/each}}
    </div>
    
    <div class="cart-summary">
        <div class="summary-line">
            <span>Subtotal:</span>
            <span>{{cart.subtotal}} TFC</span>
        </div>
        <div class="summary-line">
            <span>Estimated Deploy Time:</span>
            <span>~{{cart.estimated_deploy_time}} minutes</span>
        </div>
        <div class="summary-line total">
            <span><strong>Total:</strong></span>
            <span><strong>{{cart.total}} TFC</strong></span>
        </div>
        
        <button class="btn btn-primary btn-lg w-100" onclick="proceedToCheckout()">
            <i class="bi bi-credit-card"></i>
            Proceed to Checkout
        </button>
    </div>
</div>

2. Checkout Process

<!-- Checkout Page -->
<div class="checkout-container">
    <div class="checkout-steps">
        <div class="step active">1. Review Order</div>
        <div class="step">2. Payment</div>
        <div class="step">3. Deployment</div>
    </div>
    
    <div class="checkout-content">
        <div class="order-review">
            <h5>Order Summary</h5>
            <!-- Cart items review -->
            
            <div class="deployment-options">
                <h6>Deployment Options</h6>
                <div class="form-check">
                    <input class="form-check-input" type="radio" name="deploymentTiming" value="immediate" checked>
                    <label class="form-check-label">Deploy immediately after payment</label>
                </div>
                <div class="form-check">
                    <input class="form-check-input" type="radio" name="deploymentTiming" value="scheduled">
                    <label class="form-check-label">Schedule deployment for later</label>
                </div>
            </div>
        </div>
        
        <div class="payment-section">
            <h5>Payment Method</h5>
            <div class="payment-options">
                <div class="payment-option" onclick="selectPaymentMethod('tfc')">
                    <div class="option-header">
                        <i class="bi bi-wallet2"></i>
                        <span>Pay with TFC Balance</span>
                        <span class="balance-info">{{user.tfc_balance}} TFC available</span>
                    </div>
                    {{#if (lt user.tfc_balance cart.total)}}
                    <div class="insufficient-notice">
                        <span class="text-warning">Insufficient balance. Need {{subtract cart.total user.tfc_balance}} more TFC.</span>
                    </div>
                    {{/if}}
                </div>
                
                <div class="payment-option" onclick="selectPaymentMethod('stripe')">
                    <div class="option-header">
                        <i class="bi bi-credit-card"></i>
                        <span>Credit/Debit Card</span>
                        <span class="amount-info">${{cart.total}} USD</span>
                    </div>
                </div>
                
                <div class="payment-option" onclick="selectPaymentMethod('mixed')">
                    <div class="option-header">
                        <i class="bi bi-shuffle"></i>
                        <span>Use TFC + Credit Card</span>
                        <span class="mixed-info">{{user.tfc_balance}} TFC + ${{subtract cart.total user.tfc_balance}} USD</span>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

JavaScript Implementation

Modern App Flow Functions

// Modern App Style - Instant Purchase
async function buyNowInstant(serviceId) {
    try {
        // Check balance first
        const balance = await getUserTFCBalance();
        const service = await getServiceDetails(serviceId);
        
        if (balance >= service.price_tfc) {
            // Sufficient balance - instant deploy
            showLoadingToast('Starting deployment...');
            const result = await initiateDeployment(serviceId, 'tfc');
            
            if (result.success) {
                showSuccessToast('Deployment started! Redirecting...');
                window.location.href = `/dashboard/deployments/${result.deployment_id}`;
            }
        } else {
            // Insufficient balance - auto top-up flow
            const needed = service.price_tfc - balance;
            
            if (await isAutoTopUpEnabled()) {
                showAutoTopUpModal(needed, serviceId);
            } else {
                showManualTopUpModal(needed, serviceId);
            }
        }
    } catch (error) {
        showErrorToast('Failed to process purchase');
    }
}

// Auto Top-up Flow
async function handleAutoTopUp(amount, serviceId) {
    try {
        showLoadingToast('Processing auto top-up...');
        
        const topUpResult = await processAutoTopUp(amount);
        if (topUpResult.success) {
            showSuccessToast('Balance updated! Starting deployment...');
            
            // Proceed with deployment
            const deployResult = await initiateDeployment(serviceId, 'tfc');
            if (deployResult.success) {
                window.location.href = `/dashboard/deployments/${deployResult.deployment_id}`;
            }
        }
    } catch (error) {
        showErrorToast('Auto top-up failed');
    }
}

// Real-time Balance Updates
function startBalancePolling() {
    setInterval(async () => {
        const balance = await getUserTFCBalance();
        updateBalanceDisplay(balance);
    }, 10000); // Update every 10 seconds
}

function updateBalanceDisplay(balance) {
    document.querySelector('.balance-amount').textContent = `${balance} TFC`;
    document.querySelector('.usd-equivalent').textContent = `$${balance} USD`;
    
    // Update buy buttons state
    document.querySelectorAll('.buy-now-btn').forEach(btn => {
        const servicePrice = parseFloat(btn.dataset.price);
        if (balance >= servicePrice) {
            btn.classList.remove('insufficient-balance');
            btn.disabled = false;
        } else {
            btn.classList.add('insufficient-balance');
            btn.disabled = true;
        }
    });
}

Traditional E-commerce Functions

// Traditional E-commerce - Cart Management
class ShoppingCart {
    constructor() {
        this.items = JSON.parse(localStorage.getItem('cart_items') || '[]');
        this.updateCartDisplay();
    }
    
    addItem(serviceId, serviceName, price, specs) {
        const existingItem = this.items.find(item => item.service_id === serviceId);
        
        if (existingItem) {
            showInfoToast('Item already in cart');
            return;
        }
        
        this.items.push({
            id: generateId(),
            service_id: serviceId,
            service_name: serviceName,
            price_tfc: price,
            specs: specs,
            added_at: new Date().toISOString()
        });
        
        this.saveCart();
        this.updateCartDisplay();
        showSuccessToast('Added to cart');
    }
    
    removeItem(itemId) {
        this.items = this.items.filter(item => item.id !== itemId);
        this.saveCart();
        this.updateCartDisplay();
        showSuccessToast('Removed from cart');
    }
    
    getTotal() {
        return this.items.reduce((total, item) => total + item.price_tfc, 0);
    }
    
    async proceedToCheckout() {
        if (this.items.length === 0) {
            showErrorToast('Cart is empty');
            return;
        }
        
        // Navigate to checkout page with cart data
        const cartData = encodeURIComponent(JSON.stringify(this.items));
        window.location.href = `/checkout?cart=${cartData}`;
    }
    
    saveCart() {
        localStorage.setItem('cart_items', JSON.stringify(this.items));
    }
    
    updateCartDisplay() {
        const cartCount = document.querySelector('.cart-count');
        const cartTotal = document.querySelector('.cart-total');
        
        if (cartCount) cartCount.textContent = this.items.length;
        if (cartTotal) cartTotal.textContent = `${this.getTotal()} TFC`;
    }
}

// Checkout Process
async function processCheckout(paymentMethod, cartItems) {
    try {
        showLoadingToast('Processing order...');
        
        const orderData = {
            items: cartItems,
            payment_method: paymentMethod,
            total_tfc: cartItems.reduce((sum, item) => sum + item.price_tfc, 0)
        };
        
        const result = await fetch('/api/checkout/process', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(orderData)
        });
        
        const response = await result.json();
        
        if (response.success) {
            // Clear cart
            localStorage.removeItem('cart_items');
            
            // Redirect to order confirmation
            window.location.href = `/orders/${response.order_id}`;
        } else {
            showErrorToast(response.message);
        }
    } catch (error) {
        showErrorToast('Checkout failed');
    }
}

User Preference System

UX Mode Selection

<!-- User Preferences - UX Mode -->
<div class="ux-preference-setting">
    <h6>Shopping Experience</h6>
    <div class="form-check">
        <input class="form-check-input" type="radio" name="uxMode" value="modern" id="modernUX">
        <label class="form-check-label" for="modernUX">
            <strong>Modern App Style</strong>
            <br><small>Instant purchases with wallet top-up (like OpenRouter)</small>
        </label>
    </div>
    <div class="form-check">
        <input class="form-check-input" type="radio" name="uxMode" value="ecommerce" id="ecommerceUX">
        <label class="form-check-label" for="ecommerceUX">
            <strong>Traditional E-commerce</strong>
            <br><small>Shopping cart with checkout process</small>
        </label>
    </div>
    <div class="form-check">
        <input class="form-check-input" type="radio" name="uxMode" value="both" id="bothUX" checked>
        <label class="form-check-label" for="bothUX">
            <strong>Both Options</strong>
            <br><small>Show both "Buy Now" and "Add to Cart" buttons</small>
        </label>
    </div>
</div>

Benefits of Dual UX Approach

Modern App Flow Benefits:

  • Speed: Instant purchases
  • Simplicity: Minimal clicks
  • Mobile-friendly: Touch-optimized
  • Auto-management: Set-and-forget top-ups

Traditional E-commerce Benefits:

  • Bulk Shopping: Multiple services at once
  • Price Comparison: Review before buying
  • Familiar: Standard shopping experience
  • Planning: Schedule deployments

Combined Advantages:

  • 🎯 User Choice: Accommodate different preferences
  • 🎯 Use Case Flexibility: Quick single purchases OR planned bulk orders
  • 🎯 Market Coverage: Appeal to both app users and traditional shoppers
  • 🎯 Conversion Optimization: Multiple paths to purchase

This dual UX specification ensures the Project Mycelium appeals to both modern app users (who want instant, frictionless purchases) and traditional e-commerce users (who prefer to review and plan their purchases).