All files / web/src/lib tier-limits.ts

100% Statements 46/46
100% Branches 2/2
100% Functions 1/1
100% Lines 46/46

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 471x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x  
/**
 * Subscription tier limits — single source of truth.
 *
 * Both server-side enforcement and client-side UI (DurationSelector, etc.)
 * derive from these values. Change a limit here and it propagates everywhere.
 */
 
/** All available session duration options (minutes). */
export const DURATION_OPTIONS = [5, 10, 15, 20] as const
export type DurationOption = (typeof DURATION_OPTIONS)[number]
 
export type TierName = 'guest' | 'free' | 'family'
 
export interface TierLimits {
  maxPracticeStudents: number
  maxSessionMinutes: DurationOption
  maxSessionsPerWeek: number // Infinity = unlimited
  maxOfflineParsingPerMonth: number
}
 
export const TIER_LIMITS: Record<TierName, TierLimits> = {
  guest: {
    maxPracticeStudents: 1,
    maxSessionMinutes: 10,
    maxSessionsPerWeek: Infinity, // can't enforce — cookies clearable
    maxOfflineParsingPerMonth: 3,
  },
  free: {
    maxPracticeStudents: 1,
    maxSessionMinutes: 10,
    maxSessionsPerWeek: 5,
    maxOfflineParsingPerMonth: 3,
  },
  family: {
    maxPracticeStudents: Infinity,
    maxSessionMinutes: 20,
    maxSessionsPerWeek: Infinity,
    maxOfflineParsingPerMonth: 30,
  },
} as const
 
/** Duration options available for a given tier. */
export function durationOptionsForTier(tier: TierName): DurationOption[] {
  const max = TIER_LIMITS[tier].maxSessionMinutes
  return DURATION_OPTIONS.filter((d) => d <= max)
}