Files
hive/LICENSING_DEVELOPMENT_PLAN.md
anthonyrawlins a880b26951 feat: Implement license-aware UI for revenue optimization (Phase 3A)
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 <noreply@anthropic.com>
2025-09-01 16:20:24 +10:00

671 lines
21 KiB
Markdown

# 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<LicenseStatus> {
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<UsageMetrics> {
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<LicenseStatus> {
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<LicenseStatusDashboardProps> = ({ licenseId }) => {
const [licenseStatus, setLicenseStatus] = useState<LicenseStatus | null>(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 <div>Loading license information...</div>;
if (!licenseStatus) return <div>License information unavailable</div>;
return (
<div className="license-status-dashboard">
<LicenseStatusCard status={licenseStatus} />
<QuotaUsageCard quotas={licenseStatus.quotas} />
{licenseStatus.upgrade_suggestions?.map((suggestion, idx) => (
<UpgradeSuggestionCard key={idx} suggestion={suggestion} />
))}
</div>
);
};
```
#### 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 (
<div className="flex items-center space-x-4 text-sm">
<div className={`font-medium ${getStatusColor(licenseStatus?.status || '')}`}>
{licenseStatus?.tier?.toUpperCase()} License
</div>
<div className="text-gray-500">
{licenseStatus?.max_nodes} nodes max
</div>
<div className="text-gray-500">
Expires: {new Date(licenseStatus?.expires_at || '').toLocaleDateString()}
</div>
{licenseStatus?.status !== 'active' && (
<button className="bg-blue-600 text-white px-3 py-1 rounded text-xs hover:bg-blue-700">
Renew License
</button>
)}
</div>
);
};
```
### 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<FeatureGateProps> = ({
feature,
children,
fallback,
showUpgradePrompt = true
}) => {
const { hasFeature } = useLicenseFeatures();
const { licenseStatus } = useLicenseContext();
if (hasFeature(feature)) {
return <>{children}</>;
}
if (fallback) {
return <>{fallback}</>;
}
if (showUpgradePrompt) {
return (
<UpgradePrompt
feature={feature}
currentTier={licenseStatus?.tier || 'unknown'}
/>
);
}
return null;
};
// Usage throughout WHOOSH:
// <FeatureGate feature="advanced-analytics">
// <AdvancedAnalyticsPanel />
// </FeatureGate>
```
#### 3. Feature-Specific Gates
```typescript
// src/components/search/AdvancedSearchFilters.tsx
export const AdvancedSearchFilters: React.FC = () => {
const { canUseAdvancedSearch } = useLicenseFeatures();
return (
<FeatureGate feature="advanced-search">
<div className="advanced-filters">
{/* Advanced search filter components */}
</div>
<UpgradePrompt
feature="advanced-search"
message="Unlock advanced search filters with Standard tier"
benefits={[
"Date range filtering",
"Content type filters",
"Custom field search",
"Saved search queries"
]}
/>
</FeatureGate>
);
};
```
### 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<QuotaUsageCardProps> = ({ 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 (
<div className="quota-usage-card bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-semibold mb-4">Usage Overview</h3>
{Object.entries(quotas).map(([key, quota]) => {
const percentage = getUsagePercentage(quota.used, quota.limit);
return (
<div key={key} className="mb-4">
<div className="flex justify-between text-sm font-medium">
<span>{key.replace('_', ' ').toUpperCase()}</span>
<span>{quota.used.toLocaleString()} / {quota.limit.toLocaleString()}</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2 mt-1">
<div
className={`h-2 rounded-full ${getUsageColor(percentage)}`}
style={{ width: `${percentage}%` }}
/>
</div>
{percentage >= 80 && (
<div className="text-xs text-orange-600 mt-1">
⚠️ Approaching limit - consider upgrading
</div>
)}
</div>
);
})}
</div>
);
};
```
#### 2. Upgrade Suggestion Component
```typescript
// src/components/license/UpgradeSuggestionCard.tsx
interface UpgradeSuggestionCardProps {
suggestion: UpgradeSuggestion;
}
export const UpgradeSuggestionCard: React.FC<UpgradeSuggestionCardProps> = ({ 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 (
<div className={`upgrade-suggestion border-l-4 p-4 rounded ${getUrgencyColor(suggestion.urgency)}`}>
<div className="flex items-center justify-between">
<div>
<h4 className="font-semibold">{suggestion.reason}</h4>
<p className="text-sm text-gray-600 mt-1">
Upgrade from {suggestion.current_tier} to {suggestion.suggested_tier}
</p>
{suggestion.roi_estimate && (
<p className="text-sm font-medium text-green-600 mt-1">
Estimated ROI: {suggestion.roi_estimate}
</p>
)}
</div>
<button
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
onClick={() => handleUpgradeRequest(suggestion)}
>
Upgrade Now
</button>
</div>
<div className="mt-3">
<p className="text-sm font-medium">Benefits:</p>
<ul className="text-sm text-gray-600 mt-1">
{suggestion.benefits.map((benefit, idx) => (
<li key={idx} className="flex items-center">
<span className="text-green-500 mr-2"></span>
{benefit}
</li>
))}
</ul>
</div>
</div>
);
};
```
### 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<string>('');
const [justification, setJustification] = useState<string>('');
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 (
<Modal>
<div className="upgrade-request-modal">
<h2>Request License Upgrade</h2>
<TierComparisonTable
currentTier={licenseStatus?.tier}
highlightTier={selectedTier}
onTierSelect={setSelectedTier}
/>
<textarea
placeholder="Tell us about your use case and why you need an upgrade..."
value={justification}
onChange={(e) => setJustification(e.target.value)}
className="w-full p-3 border rounded"
/>
<UsageEvidencePanel licenseId={licenseStatus?.license_id} />
<div className="flex justify-end space-x-3 mt-6">
<button onClick={onClose}>Cancel</button>
<button
onClick={handleUpgradeRequest}
className="bg-blue-600 text-white px-6 py-2 rounded"
>
Submit Request
</button>
</div>
</div>
</Modal>
);
};
```
#### 2. Contact Sales Integration
```typescript
// src/components/license/ContactSalesWidget.tsx
export const ContactSalesWidget: React.FC = () => {
const { licenseStatus } = useLicenseContext();
const generateSalesContext = () => ({
license_id: licenseStatus?.license_id,
current_tier: licenseStatus?.tier,
usage_summary: getUsageSummary(),
pain_points: identifyPainPoints(),
upgrade_urgency: calculateUpgradeUrgency(),
});
return (
<div className="contact-sales-widget">
<h3>Need a Custom Solution?</h3>
<p>Talk to our sales team about enterprise features and pricing.</p>
<button
onClick={() => openSalesChat(generateSalesContext())}
className="bg-green-600 text-white px-4 py-2 rounded"
>
Contact Sales
</button>
<div className="text-xs text-gray-500 mt-2">
Your usage data will be shared to provide personalized recommendations
</div>
</div>
);
};
```
## Implementation Files Structure
```
WHOOSH/
├── src/
│ ├── services/
│ │ ├── licenseApi.ts # KACHING API client
│ │ └── usageTracking.ts # Usage metrics collection
│ ├── hooks/
│ │ ├── useLicenseContext.ts # License state management
│ │ └── useLicenseFeatures.ts # Feature gate logic
│ ├── components/
│ │ ├── license/
│ │ │ ├── LicenseStatusDashboard.tsx
│ │ │ ├── FeatureGate.tsx
│ │ │ ├── QuotaUsageCard.tsx
│ │ │ ├── UpgradeSuggestionCard.tsx
│ │ │ └── UpgradeRequestModal.tsx
│ │ └── layout/
│ │ └── LicenseStatusHeader.tsx
│ ├── contexts/
│ │ └── LicenseContext.tsx # Global license state
│ └── utils/
│ ├── licenseHelpers.ts # License utility functions
│ └── usageAnalytics.ts # Usage calculation helpers
├── public/
│ └── license-tiers.json # Tier comparison data
└── docs/
└── license-integration.md # Integration documentation
```
## Configuration Requirements
### Environment Variables
```bash
# KACHING integration
REACT_APP_KACHING_URL=https://kaching.chorus.services # Dev only; in prod, use backend proxy
# Do NOT expose license keys/IDs in client-side configuration
# Feature flags
REACT_APP_ENABLE_LICENSE_GATING=true
REACT_APP_ENABLE_UPGRADE_PROMPTS=true
# Sales integration
REACT_APP_SALES_CHAT_URL=https://sales.chorus.services/chat
REACT_APP_SALES_EMAIL=sales@chorus.services
```
### License Context Configuration
```typescript
// src/config/licenseConfig.ts
export const LICENSE_CONFIG = {
tiers: {
evaluation: {
display_name: 'Evaluation',
max_search_results: 50,
features: ['basic-search'],
color: 'gray'
},
standard: {
display_name: 'Standard',
max_search_results: 1000,
features: ['basic-search', 'advanced-search', 'analytics'],
color: 'blue'
},
enterprise: {
display_name: 'Enterprise',
max_search_results: -1, // unlimited
features: ['basic-search', 'advanced-search', 'analytics', 'bulk-operations', 'enterprise-support'],
color: 'purple'
}
},
upgrade_thresholds: {
search_requests: 0.8, // Show upgrade at 80% quota usage
storage_gb: 0.9, // Show upgrade at 90% storage usage
api_calls: 0.85 // Show upgrade at 85% API usage
}
};
```
## Testing Strategy
### Unit Tests Required
- Feature gate hook functionality
- License status display components
- Quota usage calculations
- Upgrade suggestion logic
### Integration Tests Required
- End-to-end license status fetching
- Feature gating across different components
- Upgrade request workflow
- Usage tracking integration
### User Experience Tests
- License tier upgrade flows
- Feature restriction user messaging
- Quota limit notifications
- Sales contact workflows
## Success Criteria
### Phase 3A Success
- [ ] License status displayed prominently in UI
- [ ] Real-time quota usage monitoring
- [ ] Tier information clearly communicated to users
### Phase 3B Success
- [ ] Features properly gated based on license tier
- [ ] Upgrade prompts appear for restricted features
- [ ] Clear messaging about tier limitations
### Phase 3C Success
- [ ] Quota usage alerts trigger at appropriate thresholds
- [ ] Upgrade suggestions appear based on usage patterns
- [ ] Usage trends drive automated upselling
### Phase 3D Success
- [ ] Self-service upgrade request workflow functional
- [ ] Sales team integration captures relevant context
- [ ] Customer can understand upgrade benefits clearly
### Overall Success
- [ ] **Increased license upgrade conversion rate**
- [ ] Users aware of their license limitations
- [ ] Proactive upgrade suggestions drive revenue
- [ ] Seamless integration with KACHING license authority
## Business Impact Metrics
### Revenue Metrics
- License upgrade conversion rate (target: 15% monthly)
- Average revenue per user increase (target: 25% annually)
- Feature adoption rates by tier
### User Experience Metrics
- License status awareness (target: 90% of users know their tier)
- Time to upgrade after quota warning (target: <7 days)
- Support tickets related to license confusion (target: <5% of total)
### Technical Metrics
- License API response times (target: <200ms)
- Feature gate reliability (target: 99.9% uptime)
- Quota usage accuracy (target: 100% data integrity)
## Dependencies
- **KACHING Phase 1 Complete**: Requires license server with quota APIs
- **User Authentication**: Must identify users to fetch license status
- **Usage Tracking**: Requires instrumentation to measure quota consumption
## Security Considerations
1. **License ID Protection**: Never expose license keys/IDs in client-side code; resolve license_id server-side
2. **API Authentication**: Secure backendKACHING with service credentials; frontend talks only to WHOOSH backend
3. **Feature Bypass Prevention**: Enforce entitlements server-side for any sensitive operations
4. **Usage Data Privacy**: Comply with data protection regulations for usage tracking
This plan transforms WHOOSH from license-unaware to a comprehensive license-integrated experience that drives revenue optimization and user satisfaction.