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>
This commit is contained in:
670
LICENSING_DEVELOPMENT_PLAN.md
Normal file
670
LICENSING_DEVELOPMENT_PLAN.md
Normal file
@@ -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<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 backend→KACHING 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.
|
||||
Reference in New Issue
Block a user