From a880b2695134dda4ab6707be80333e53f2823605 Mon Sep 17 00:00:00 2001 From: anthonyrawlins Date: Mon, 1 Sep 2025 16:20:24 +1000 Subject: [PATCH] feat: Implement license-aware UI for revenue optimization (Phase 3A) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Business Objective: Transform WHOOSH from license-unaware to comprehensive license-integrated experience that drives upgrade conversions and maximizes customer lifetime value through usage visibility. Implementation Summary: 1. SECURE BACKEND PROXY INTEGRATION: - License API proxy endpoints (/api/license/status, /api/license/quotas) - Server-side license ID resolution (no frontend exposure) - Mock data support for development and testing - Intelligent upgrade suggestion algorithms 2. COMPREHENSIVE FRONTEND LICENSE INTEGRATION: - License API Client with caching and error handling - Global License Context for state management - License Status Header for always-visible tier information - Feature Gate Component for conditional rendering - License Dashboard with quotas, features, upgrade suggestions - Upgrade Prompt Components for revenue optimization 3. APPLICATION-WIDE INTEGRATION: - License Provider integrated into App context hierarchy - License status header in main navigation - License dashboard route at /license - Example feature gates in Analytics page - Version bump: → 1.2.0 Key Business Benefits: ✅ Revenue Optimization: Strategic feature gating drives conversions ✅ User Trust: Transparent license information builds confidence ✅ Proactive Upgrades: Usage-based suggestions with ROI estimates ✅ Self-Service: Clear upgrade paths reduce sales friction Security-First Design: 🔒 All license operations server-side via proxy 🔒 No sensitive license data exposed to frontend 🔒 Feature enforcement at API level prevents bypass 🔒 Graceful degradation for license API failures Technical Implementation: - React 18+ with TypeScript and modern hooks - Context API for license state management - Tailwind CSS following existing patterns - Backend proxy pattern for security compliance - Comprehensive error handling and loading states Files Created/Modified: Backend: - /backend/app/api/license.py - Complete license proxy API - /backend/app/main.py - Router integration Frontend: - /frontend/src/services/licenseApi.ts - API client with caching - /frontend/src/contexts/LicenseContext.tsx - Global license state - /frontend/src/hooks/useLicenseFeatures.ts - Feature checking logic - /frontend/src/components/license/* - Complete license UI components - /frontend/src/App.tsx - Context integration and routing - /frontend/package.json - Version bump to 1.2.0 This Phase 3A implementation provides the complete foundation for license-aware user experiences, driving revenue optimization through intelligent feature gating and upgrade suggestions while maintaining excellent UX and security best practices. Ready for KACHING integration and Phase 3B advanced features. 🤖 Generated with Claude Code (claude.ai/code) Co-Authored-By: Claude --- LICENSING_DEVELOPMENT_PLAN.md | 670 ++++++++++++++++++ PHASE3A_IMPLEMENTATION_SUMMARY.md | 293 ++++++++ backend/app/api/license.py | 591 +++++++++++++++ backend/app/main.py | 7 +- frontend/package.json | 2 +- frontend/src/App.tsx | 29 +- frontend/src/components/Layout.tsx | 8 +- .../src/components/license/FeatureGate.tsx | 403 +++++++++++ .../components/license/LicenseDashboard.tsx | 492 +++++++++++++ .../license/LicenseStatusHeader.tsx | 344 +++++++++ .../src/components/license/UpgradePrompt.tsx | 335 +++++++++ frontend/src/contexts/LicenseContext.tsx | 417 +++++++++++ frontend/src/hooks/useLicenseFeatures.ts | 467 ++++++++++++ frontend/src/pages/Analytics.tsx | 36 +- frontend/src/services/licenseApi.ts | 438 ++++++++++++ 15 files changed, 4511 insertions(+), 21 deletions(-) create mode 100644 LICENSING_DEVELOPMENT_PLAN.md create mode 100644 PHASE3A_IMPLEMENTATION_SUMMARY.md create mode 100644 backend/app/api/license.py create mode 100644 frontend/src/components/license/FeatureGate.tsx create mode 100644 frontend/src/components/license/LicenseDashboard.tsx create mode 100644 frontend/src/components/license/LicenseStatusHeader.tsx create mode 100644 frontend/src/components/license/UpgradePrompt.tsx create mode 100644 frontend/src/contexts/LicenseContext.tsx create mode 100644 frontend/src/hooks/useLicenseFeatures.ts create mode 100644 frontend/src/services/licenseApi.ts diff --git a/LICENSING_DEVELOPMENT_PLAN.md b/LICENSING_DEVELOPMENT_PLAN.md new file mode 100644 index 00000000..baa2249e --- /dev/null +++ b/LICENSING_DEVELOPMENT_PLAN.md @@ -0,0 +1,670 @@ +# WHOOSH Licensing Development Plan + +**Date**: 2025-09-01 +**Branch**: `feature/license-gating-integration` +**Status**: Ready for implementation (depends on KACHING Phase 1) +**Priority**: MEDIUM - User experience and upselling integration + +## Executive Summary + +WHOOSH currently has **zero CHORUS licensing integration**. The system operates without license validation, feature gating, or upselling workflows. This plan integrates WHOOSH with KACHING license authority to provide license-aware user experiences and revenue optimization. + +## Current State Analysis + +### ✅ Existing Infrastructure +- React-based web application with modern UI components +- Search and indexing functionality +- User authentication and session management +- API integration capabilities + +### ❌ Missing License Integration +- **No license status display** - Users unaware of their tier/limits +- **No feature gating** - All features available regardless of license +- **No upgrade workflows** - No upselling or upgrade prompts +- **No usage tracking** - No integration with KACHING telemetry +- **No quota visibility** - Users can't see usage limits or consumption + +### Business Impact +- **Zero upselling capability** - No way to drive license upgrades +- **No usage awareness** - Customers don't know they're approaching limits +- **No tier differentiation** - Premium features not monetized +- **Revenue leakage** - Advanced features available to basic tier users + +## Development Phases + +### Phase 3A: License Status Integration (PRIORITY 1) +**Goal**: Display license information and status throughout WHOOSH UI + +#### 1. License API Client Implementation +```typescript +// src/services/licenseApi.ts +export interface LicenseStatus { + license_id: string; + status: 'active' | 'suspended' | 'expired' | 'cancelled'; + tier: 'evaluation' | 'standard' | 'enterprise'; + features: string[]; + max_nodes: number; + expires_at: string; + quotas: { + search_requests: { used: number; limit: number }; + storage_gb: { used: number; limit: number }; + api_calls: { used: number; limit: number }; + }; + upgrade_suggestions?: UpgradeSuggestion[]; +} + +export interface UpgradeSuggestion { + reason: string; + current_tier: string; + suggested_tier: string; + benefits: string[]; + roi_estimate?: string; + urgency: 'low' | 'medium' | 'high'; +} + +class LicenseApiClient { + private baseUrl: string; + + constructor(kachingUrl: string) { + this.baseUrl = kachingUrl; + } + + async getLicenseStatus(licenseId: string): Promise { + const response = await fetch(`${this.baseUrl}/v1/license/status/${licenseId}`); + if (!response.ok) { + throw new Error('Failed to fetch license status'); + } + return response.json(); + } + + async getUsageMetrics(licenseId: string): Promise { + const response = await fetch(`${this.baseUrl}/v1/usage/metrics/${licenseId}`); + return response.json(); + } +} +``` + +#### Backend Proxy (required in production) +To avoid exposing licensing endpoints/IDs client-side and to enforce server-side checks, WHOOSH should proxy KACHING via its own backend: + +```python +# backend/app/api/license.py (FastAPI example) +@router.get("/api/license/status") +async def get_status(user=Depends(auth)): + license_id = await resolve_license_id_for_org(user.org_id) + res = await kaching.get(f"/v1/license/status/{license_id}") + return res.json() + +@router.get("/api/license/quotas") +async def get_quotas(user=Depends(auth)): + license_id = await resolve_license_id_for_org(user.org_id) + res = await kaching.get(f"/v1/license/{license_id}/quotas") + return res.json() +``` + +And in the React client call the WHOOSH backend instead of KACHING directly: + +```typescript +// src/services/licenseApi.ts (frontend) +export async function fetchLicenseStatus(): Promise { + const res = await fetch("/api/license/status") + if (!res.ok) throw new Error("Failed to fetch license status") + return res.json() +} +``` + +#### 2. License Status Dashboard Component +```typescript +// src/components/license/LicenseStatusDashboard.tsx +interface LicenseStatusDashboardProps { + licenseId: string; +} + +export const LicenseStatusDashboard: React.FC = ({ licenseId }) => { + const [licenseStatus, setLicenseStatus] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchLicenseStatus = async () => { + try { + // In production, call WHOOSH backend proxy endpoints + const status = await fetchLicenseStatus(); + setLicenseStatus(status); + } catch (error) { + console.error('Failed to fetch license status:', error); + } finally { + setLoading(false); + } + }; + + fetchLicenseStatus(); + // Refresh every 5 minutes + const interval = setInterval(fetchLicenseStatus, 5 * 60 * 1000); + return () => clearInterval(interval); + }, [licenseId]); + + if (loading) return
Loading license information...
; + if (!licenseStatus) return
License information unavailable
; + + return ( +
+ + + {licenseStatus.upgrade_suggestions?.map((suggestion, idx) => ( + + ))} +
+ ); +}; +``` + +#### 3. License Status Header Component +```typescript +// src/components/layout/LicenseStatusHeader.tsx +export const LicenseStatusHeader: React.FC = () => { + const { licenseStatus } = useLicenseContext(); + + const getStatusColor = (status: string) => { + switch (status) { + case 'active': return 'text-green-600'; + case 'suspended': return 'text-red-600'; + case 'expired': return 'text-orange-600'; + default: return 'text-gray-600'; + } + }; + + return ( +
+
+ {licenseStatus?.tier?.toUpperCase()} License +
+
+ {licenseStatus?.max_nodes} nodes max +
+
+ Expires: {new Date(licenseStatus?.expires_at || '').toLocaleDateString()} +
+ {licenseStatus?.status !== 'active' && ( + + )} +
+ ); +}; +``` + +### Phase 3B: Feature Gating Implementation (PRIORITY 2) +**Goal**: Restrict features based on license tier and show upgrade prompts + +#### 1. Feature Gate Hook +```typescript +// src/hooks/useLicenseFeatures.ts +export const useLicenseFeatures = () => { + const { licenseStatus } = useLicenseContext(); + + const hasFeature = (feature: string): boolean => { + return licenseStatus?.features?.includes(feature) || false; + }; + + const canUseAdvancedSearch = (): boolean => { + return hasFeature('advanced-search'); + }; + + const canUseAnalytics = (): boolean => { + return hasFeature('advanced-analytics'); + }; + + const canUseBulkOperations = (): boolean => { + return hasFeature('bulk-operations'); + }; + + const getMaxSearchResults = (): number => { + if (hasFeature('enterprise-search')) return 10000; + if (hasFeature('advanced-search')) return 1000; + return 100; // Basic tier + }; + + return { + hasFeature, + canUseAdvancedSearch, + canUseAnalytics, + canUseBulkOperations, + getMaxSearchResults, + }; +}; +``` + +#### 2. Feature Gate Component +```typescript +// src/components/license/FeatureGate.tsx +interface FeatureGateProps { + feature: string; + children: React.ReactNode; + fallback?: React.ReactNode; + showUpgradePrompt?: boolean; +} + +export const FeatureGate: React.FC = ({ + feature, + children, + fallback, + showUpgradePrompt = true +}) => { + const { hasFeature } = useLicenseFeatures(); + const { licenseStatus } = useLicenseContext(); + + if (hasFeature(feature)) { + return <>{children}; + } + + if (fallback) { + return <>{fallback}; + } + + if (showUpgradePrompt) { + return ( + + ); + } + + return null; +}; + +// Usage throughout WHOOSH: +// +// +// +``` + +#### 3. Feature-Specific Gates +```typescript +// src/components/search/AdvancedSearchFilters.tsx +export const AdvancedSearchFilters: React.FC = () => { + const { canUseAdvancedSearch } = useLicenseFeatures(); + + return ( + +
+ {/* Advanced search filter components */} +
+ +
+ ); +}; +``` + +### Phase 3C: Quota Monitoring & Alerts (PRIORITY 3) +**Goal**: Show usage quotas and proactive upgrade suggestions + +#### 1. Quota Usage Components +```typescript +// src/components/license/QuotaUsageCard.tsx +interface QuotaUsageCardProps { + quotas: LicenseStatus['quotas']; +} + +export const QuotaUsageCard: React.FC = ({ quotas }) => { + const getUsagePercentage = (used: number, limit: number): number => { + return Math.round((used / limit) * 100); + }; + + const getUsageColor = (percentage: number): string => { + if (percentage >= 90) return 'bg-red-500'; + if (percentage >= 75) return 'bg-yellow-500'; + return 'bg-green-500'; + }; + + return ( +
+

Usage Overview

+ + {Object.entries(quotas).map(([key, quota]) => { + const percentage = getUsagePercentage(quota.used, quota.limit); + + return ( +
+
+ {key.replace('_', ' ').toUpperCase()} + {quota.used.toLocaleString()} / {quota.limit.toLocaleString()} +
+
+
+
+ {percentage >= 80 && ( +
+ ⚠️ Approaching limit - consider upgrading +
+ )} +
+ ); + })} +
+ ); +}; +``` + +#### 2. Upgrade Suggestion Component +```typescript +// src/components/license/UpgradeSuggestionCard.tsx +interface UpgradeSuggestionCardProps { + suggestion: UpgradeSuggestion; +} + +export const UpgradeSuggestionCard: React.FC = ({ suggestion }) => { + const getUrgencyColor = (urgency: string): string => { + switch (urgency) { + case 'high': return 'border-red-500 bg-red-50'; + case 'medium': return 'border-yellow-500 bg-yellow-50'; + default: return 'border-blue-500 bg-blue-50'; + } + }; + + return ( +
+
+
+

{suggestion.reason}

+

+ Upgrade from {suggestion.current_tier} to {suggestion.suggested_tier} +

+ {suggestion.roi_estimate && ( +

+ Estimated ROI: {suggestion.roi_estimate} +

+ )} +
+ +
+ +
+

Benefits:

+
    + {suggestion.benefits.map((benefit, idx) => ( +
  • + + {benefit} +
  • + ))} +
+
+
+ ); +}; +``` + +### Phase 3D: Self-Service Upgrade Workflows (PRIORITY 4) +**Goal**: Enable customers to upgrade licenses directly from WHOOSH + +#### 1. Upgrade Request Modal +```typescript +// src/components/license/UpgradeRequestModal.tsx +export const UpgradeRequestModal: React.FC = () => { + const [selectedTier, setSelectedTier] = useState(''); + const [justification, setJustification] = useState(''); + + const handleUpgradeRequest = async () => { + const request = { + current_tier: licenseStatus?.tier, + requested_tier: selectedTier, + justification, + usage_evidence: await getUsageEvidence(), + contact_email: userEmail, + }; + + // Send to KACHING upgrade request endpoint + await licenseApi.requestUpgrade(request); + + // Show success message and close modal + showNotification('Upgrade request submitted successfully!'); + }; + + return ( + +
+

Request License Upgrade

+ + + +