feat: update purchase plans

This commit is contained in:
ivannoskov
2026-02-10 11:42:17 +03:00
parent 1826483750
commit d84f86a988
9 changed files with 1289 additions and 1134 deletions

205
lib/types/permission.ts Normal file
View File

@@ -0,0 +1,205 @@
import type { PermissionKey, Permission, PermissionScope, PermissionScopeType } from "./api";
export const PERMISSION_GROUPS = {
admin: ["admin_full"] as const,
projects: ["projects_read", "projects_write"] as const,
creatives: ["creatives_read", "creatives_write"] as const,
placements: ["placements_read", "placements_write"] as const,
analytics: ["analytics_read", "analytics_without_clicks", "analytics_own_creatives"] as const,
} as const;
export type PermissionGroup = keyof typeof PERMISSION_GROUPS;
export const PERMISSION_ICONS: Record<PermissionKey, string> = {
admin_full: "👑",
projects_read: "📁",
projects_write: "📁",
creatives_read: "🎨",
creatives_write: "🎨",
placements_read: "📋",
placements_write: "📋",
analytics_read: "📊",
analytics_without_clicks: "📊",
analytics_own_creatives: "📊",
};
export const PERMISSION_LABELS: Record<PermissionKey, string> = {
admin_full: "Полный доступ к администрированию",
projects_read: "Просмотр проектов",
projects_write: "Управление проектами",
creatives_read: "Просмотр креативов",
creatives_write: "Управление креативами",
placements_read: "Просмотр размещений",
placements_write: "Управление размещениями",
analytics_read: "Просмотр аналитики",
analytics_without_clicks: "Аналитика без кликов",
analytics_own_creatives: "Аналитика своих креативов",
};
export function hasGlobalPermission(permissions: Permission[], key: PermissionKey): boolean {
return permissions.some((p) => p.key === key && (!p.scopes || p.scopes.length === 0));
}
export function hasScopedPermission(
permissions: Permission[],
key: PermissionKey,
scopeType: PermissionScopeType,
scopeId: string
): boolean {
const perm = permissions.find((p) => p.key === key);
if (!perm || !perm.scopes) return false;
return perm.scopes.some((s) => s.type === scopeType && s.id === scopeId);
}
export function getScopesForPermission(
permissions: Permission[],
key: PermissionKey,
scopeType: PermissionScopeType
): string[] {
const perm = permissions.find((p) => p.key === key);
if (!perm || !perm.scopes) return [];
return perm.scopes.filter((s) => s.type === scopeType).map((s) => s.id);
}
export function addGlobalPermission(permissions: Permission[], key: PermissionKey): Permission[] {
const perm = permissions.find((p) => p.key === key);
if (perm) {
if (!perm.scopes || perm.scopes.length === 0) {
return permissions;
}
const newPerms = permissions.filter((p) => p.key !== key);
return [...newPerms, { key, scopes: undefined }];
}
return [...permissions, { key, scopes: undefined }];
}
export function removePermission(permissions: Permission[], key: PermissionKey): Permission[] {
return permissions.filter((p) => p.key !== key);
}
export function setScopedPermission(
permissions: Permission[],
key: PermissionKey,
scopeType: PermissionScopeType,
scopeIds: string[]
): Permission[] {
const existing = permissions.find((p) => p.key === key);
const otherPerms = permissions.filter((p) => p.key !== key);
if (scopeIds.length === 0) {
return otherPerms;
}
const existingScopes = existing?.scopes?.filter((s) => s.type !== scopeType) || [];
const newScopes = [...existingScopes, ...scopeIds.map((id) => ({ type: scopeType, id }))];
return [...otherPerms, { key, scopes: newScopes }];
}
export function toggleScopedPermission(
permissions: Permission[],
key: PermissionKey,
scopeType: PermissionScopeType,
scopeId: string
): Permission[] {
const existing = permissions.find((p) => p.key === key);
const otherPerms = permissions.filter((p) => p.key !== key);
if (!existing) {
return [...permissions, { key, scopes: [{ type: scopeType, id: scopeId }] }];
}
const scopes = existing.scopes || [];
const hasScope = scopes.some((s) => s.type === scopeType && s.id === scopeId);
if (hasScope) {
const filtered = scopes.filter((s) => !(s.type === scopeType && s.id === scopeId));
if (filtered.length === 0) {
return otherPerms;
}
return [...otherPerms, { key, scopes: filtered }];
}
return [...otherPerms, { key, scopes: [...scopes, { type: scopeType, id: scopeId }] }];
}
export function mergePermissions(
current: Permission[],
updates: Permission[]
): Permission[] {
const result = new Map<string, Permission>();
for (const perm of current) {
result.set(perm.key, perm);
}
for (const update of updates) {
const existing = result.get(update.key);
if (!existing) {
result.set(update.key, update);
} else if (!update.scopes || update.scopes.length === 0) {
result.set(update.key, update);
} else {
const existingScopes = existing.scopes || [];
const newScopes = new Map<string, PermissionScope>();
for (const scope of existingScopes) {
newScopes.set(`${scope.type}:${scope.id}`, scope);
}
for (const scope of update.scopes) {
newScopes.set(`${scope.type}:${scope.id}`, scope);
}
result.set(update.key, { key: update.key, scopes: Array.from(newScopes.values()) });
}
}
return Array.from(result.values());
}
export function calculatePermissionDiff(
original: Permission[],
updated: Permission[]
): { added: Permission[]; removed: Permission[]; modified: Permission[] } {
const added: Permission[] = [];
const removed: Permission[] = [];
const modified: Permission[] = [];
const originalKeys = new Set(original.map((p) => p.key));
const updatedKeys = new Set(updated.map((p) => p.key));
for (const perm of updated) {
if (!originalKeys.has(perm.key)) {
added.push(perm);
}
}
for (const perm of original) {
if (!updatedKeys.has(perm.key)) {
removed.push(perm);
}
}
for (const perm of updated) {
const orig = original.find((p) => p.key === perm.key);
if (orig && JSON.stringify(orig.scopes) !== JSON.stringify(perm.scopes)) {
modified.push(perm);
}
}
return { added, removed, modified };
}
export function isPermissionFullyGlobal(permissions: Permission[], key: PermissionKey): boolean {
const perm = permissions.find((p) => p.key === key);
return perm !== undefined && (!perm.scopes || perm.scopes.length === 0);
}
export function getAllScopeIdsForPermission(
permissions: Permission[],
key: PermissionKey,
scopeType: PermissionScopeType
): string[] {
const perm = permissions.find((p) => p.key === key);
if (!perm || !perm.scopes) return [];
return perm.scopes.filter((s) => s.type === scopeType).map((s) => s.id);
}