templates2.Saas.Next/lib/org.ts
w4c-templates 6538da5797
Some checks are pending
CI / quality (push) Waiting to run
chore(template): publish Saas.Next
2026-10-11 23:24:56 +03:00

51 lines
1.5 KiB
TypeScript

import { getPlan, type PlanId } from './plans';
export type OrgRole = 'owner' | 'admin' | 'member';
export const ORG_ROLES: OrgRole[] = ['owner', 'admin', 'member'];
export function isOrgRole(value: unknown): value is OrgRole {
return value === 'owner' || value === 'admin' || value === 'member';
}
export function roleRank(role: OrgRole): number {
return role === 'owner' ? 3 : role === 'admin' ? 2 : 1;
}
export function canManageMembers(role: unknown): boolean {
return isOrgRole(role) && roleRank(role) >= 2;
}
export function canManageBilling(role: unknown): boolean {
return role === 'owner';
}
export function seatLimit(plan: unknown): number {
return getPlan(plan).limits.seats;
}
/** True when one more member may be added (the count is checked before the insert). */
export function withinSeats(plan: unknown, currentMembers: number): boolean {
return currentMembers < seatLimit(plan);
}
export const FEATURES = {
free: ['dashboard', 'members', 'billing'],
pro: ['dashboard', 'members', 'billing', 'reports'],
} as const;
export type Feature = 'dashboard' | 'members' | 'billing' | 'reports';
export function canUseFeature(plan: unknown, feature: Feature): boolean {
const id = (getPlan(plan).id ?? 'free') as PlanId;
return (FEATURES[id] as readonly string[]).includes(feature);
}
export function slugifyOrg(name: string): string {
return name
.toLowerCase()
.trim()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
.slice(0, 40);
}