- Add NHTSA client for VIN decoding with caching and validation - Add POST /api/vehicles/decode-vin endpoint with tier gating - Add dropdown matching service with confidence levels - Add decode button to VehicleForm with tier check - Responsive layout: stacks on mobile, inline on desktop - Only populate empty fields (preserve user input) Backend: - NHTSAClient with 5s timeout, VIN validation, vin_cache table - Tier gating with 'vehicle.vinDecode' feature key (Pro+) - Tiered matching: high (exact), medium (normalized), none Frontend: - Decode button with loading state and error handling - UpgradeRequiredDialog for free tier users - Mobile-first responsive layout 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
79 lines
2.2 KiB
TypeScript
79 lines
2.2 KiB
TypeScript
/**
|
|
* @ai-summary Feature tier configuration and utilities
|
|
* @ai-context Defines feature-to-tier mapping for gating premium features
|
|
*/
|
|
|
|
import { SubscriptionTier } from '../../features/user-profile/domain/user-profile.types';
|
|
|
|
// Tier hierarchy: higher number = higher access level
|
|
export const TIER_LEVELS: Record<SubscriptionTier, number> = {
|
|
free: 0,
|
|
pro: 1,
|
|
enterprise: 2,
|
|
} as const;
|
|
|
|
// Feature configuration interface
|
|
export interface FeatureConfig {
|
|
minTier: SubscriptionTier;
|
|
name: string;
|
|
upgradePrompt: string;
|
|
}
|
|
|
|
// Feature registry - add new gated features here
|
|
export const FEATURE_TIERS: Record<string, FeatureConfig> = {
|
|
'document.scanMaintenanceSchedule': {
|
|
minTier: 'pro',
|
|
name: 'Scan for Maintenance Schedule',
|
|
upgradePrompt: 'Upgrade to Pro to automatically extract maintenance schedules from your vehicle manuals.',
|
|
},
|
|
'vehicle.vinDecode': {
|
|
minTier: 'pro',
|
|
name: 'VIN Decode',
|
|
upgradePrompt: 'Upgrade to Pro to automatically decode VIN and populate vehicle details from the NHTSA database.',
|
|
},
|
|
} as const;
|
|
|
|
/**
|
|
* Get numeric level for a subscription tier
|
|
*/
|
|
export function getTierLevel(tier: SubscriptionTier): number {
|
|
return TIER_LEVELS[tier] ?? 0;
|
|
}
|
|
|
|
/**
|
|
* Check if a user tier can access a feature
|
|
* Higher tiers inherit access to all lower tier features
|
|
*/
|
|
export function canAccessFeature(userTier: SubscriptionTier, featureKey: string): boolean {
|
|
const feature = FEATURE_TIERS[featureKey];
|
|
if (!feature) {
|
|
// Unknown features are accessible by all (fail open for unlisted features)
|
|
return true;
|
|
}
|
|
return getTierLevel(userTier) >= getTierLevel(feature.minTier);
|
|
}
|
|
|
|
/**
|
|
* Get the minimum required tier for a feature
|
|
* Returns null if feature is not gated
|
|
*/
|
|
export function getRequiredTier(featureKey: string): SubscriptionTier | null {
|
|
const feature = FEATURE_TIERS[featureKey];
|
|
return feature?.minTier ?? null;
|
|
}
|
|
|
|
/**
|
|
* Get full feature configuration
|
|
* Returns undefined if feature is not registered
|
|
*/
|
|
export function getFeatureConfig(featureKey: string): FeatureConfig | undefined {
|
|
return FEATURE_TIERS[featureKey];
|
|
}
|
|
|
|
/**
|
|
* Get all feature configurations (for API endpoint)
|
|
*/
|
|
export function getAllFeatureConfigs(): Record<string, FeatureConfig> {
|
|
return { ...FEATURE_TIERS };
|
|
}
|