54 lines
2.0 KiB
TypeScript
54 lines
2.0 KiB
TypeScript
/**
|
|
* Tier-gating helpers (Session 19).
|
|
*
|
|
* Shared, declarative answers to the questions every list-rendering
|
|
* component asks: "should this user see everything, or a teaser?"
|
|
* Centralized so we don't drift across components (one screen
|
|
* showing 3 rows + upgrade hint, another showing 5 rows + lock
|
|
* icon, a third showing all rows with a partial blur — that path
|
|
* leads to chaos).
|
|
*
|
|
* The actual SECURITY boundary on tier-locked data lives on the
|
|
* server (the API routes that produce these lists already filter by
|
|
* the bearer token's tier). These helpers govern presentation only,
|
|
* matching the server-supplied limit so the UI doesn't promise more
|
|
* than the API will deliver.
|
|
*
|
|
* Free users see top 3. Paid (analyst + desk) see everything.
|
|
* Africa tier (entry-level subscription) also sees everything for
|
|
* lists; gradient features live behind canSeeGradeDetails.
|
|
*/
|
|
|
|
export type Tier = 'free' | 'africa' | 'analyst' | 'desk' | string;
|
|
|
|
const PAID_TIERS: ReadonlySet<string> = new Set(['analyst', 'desk']);
|
|
const FULL_LIST_TIERS: ReadonlySet<string> = new Set(['africa', 'analyst', 'desk']);
|
|
|
|
const FREE_VISIBLE_COUNT = 3;
|
|
|
|
export function canSeeFullLists(tier: Tier): boolean {
|
|
if (!tier) return false;
|
|
return FULL_LIST_TIERS.has(String(tier).toLowerCase());
|
|
}
|
|
|
|
export function canSeeGradeDetails(tier: Tier): boolean {
|
|
if (!tier) return false;
|
|
return PAID_TIERS.has(String(tier).toLowerCase());
|
|
}
|
|
|
|
export function getVisibleCount(tier: Tier, totalCount: number): number {
|
|
if (totalCount <= 0) return 0;
|
|
if (canSeeFullLists(tier)) return totalCount;
|
|
return Math.min(FREE_VISIBLE_COUNT, totalCount);
|
|
}
|
|
|
|
/**
|
|
* Inverse helper for UX copy — "Upgrade to see N more results."
|
|
* Returns 0 when the tier already sees everything (so the upsell
|
|
* line can short-circuit without arithmetic).
|
|
*/
|
|
export function getHiddenCount(tier: Tier, totalCount: number): number {
|
|
if (canSeeFullLists(tier)) return 0;
|
|
return Math.max(0, totalCount - FREE_VISIBLE_COUNT);
|
|
}
|