59 lines
1.6 KiB
TypeScript
59 lines
1.6 KiB
TypeScript
export type PlanId = 'free' | 'pro';
|
|
|
|
export type Plan = {
|
|
id: PlanId;
|
|
name: string;
|
|
price: number;
|
|
/** Monthly included usage per metric. */
|
|
limits: { seats: number; apiCalls: number };
|
|
features: string[];
|
|
};
|
|
|
|
export const PLANS: Record<PlanId, Plan> = {
|
|
free: {
|
|
id: 'free',
|
|
name: 'Free',
|
|
price: 0,
|
|
limits: { seats: 2, apiCalls: 1_000 },
|
|
features: ['Up to 2 members', '1,000 API calls / month', 'Community support'],
|
|
},
|
|
pro: {
|
|
id: 'pro',
|
|
name: 'Pro',
|
|
price: 29,
|
|
limits: { seats: 25, apiCalls: 100_000 },
|
|
features: ['Up to 25 members', '100,000 API calls / month', 'Priority support'],
|
|
},
|
|
};
|
|
|
|
export const PLAN_IDS: PlanId[] = ['free', 'pro'];
|
|
|
|
export function isPlanId(value: unknown): value is PlanId {
|
|
return value === 'free' || value === 'pro';
|
|
}
|
|
|
|
export function getPlan(value: unknown): Plan {
|
|
return isPlanId(value) ? PLANS[value] : PLANS.free;
|
|
}
|
|
|
|
export function withinLimit(plan: unknown, metric: keyof Plan['limits'], used: number): boolean {
|
|
return used < getPlan(plan).limits[metric];
|
|
}
|
|
|
|
export function formatCurrency(amount: number, currency = 'USD'): string {
|
|
return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount);
|
|
}
|
|
|
|
export function formatDate(value: Date | string): string {
|
|
const d = typeof value === 'string' ? new Date(value) : value;
|
|
return new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' }).format(d);
|
|
}
|
|
|
|
export function slugify(value: string): string {
|
|
return value
|
|
.toLowerCase()
|
|
.trim()
|
|
.replace(/[^a-z0-9]+/g, '-')
|
|
.replace(/^-+|-+$/g, '');
|
|
}
|