-
-
-
- {filters.sort && (
-
-
-
-
- )}
- {filters.sort && (
-
diff --git a/components/layout/app-sidebar.tsx b/components/layout/app-sidebar.tsx
index 00b431b..b016958 100644
--- a/components/layout/app-sidebar.tsx
+++ b/components/layout/app-sidebar.tsx
@@ -100,7 +100,7 @@ interface NavItem {
url: string;
icon?: LucideIcon;
isActive?: boolean;
- requiredPermission?: "projects_read" | "placements_read" | "analytics_read" | "admin_full";
+ requiredPermission?: "admin_full" | "projects_read" | "projects_write" | "creatives_read" | "creatives_write" | "placements_read" | "placements_write" | "analytics_read" | "analytics_without_clicks" | "analytics_own_creatives";
tooltip?: string;
items?: {
title: string;
@@ -404,8 +404,8 @@ export function AppSidebar({ ...props }: React.ComponentProps
) {
});
}
- // 5. Аналитика — только подразделы
- if (hasPermission("analytics_read")) {
+ // 5. Аналитика — проверяем любой из аналитических доступов
+ if (hasPermission("analytics_read") || hasPermission("analytics_without_clicks") || hasPermission("analytics_own_creatives")) {
items.push({
title: "Аналитика",
url: buildRoute.analytics(workspaceId),
diff --git a/components/providers/workspace-provider.tsx b/components/providers/workspace-provider.tsx
index 84bb1b7..12a11ba 100644
--- a/components/providers/workspace-provider.tsx
+++ b/components/providers/workspace-provider.tsx
@@ -53,7 +53,7 @@ interface WorkspaceContextType {
// Permissions
permissions: Permission[];
- hasPermission: (key: PermissionKey, projectId?: string) => boolean;
+ hasPermission: (key: PermissionKey, projectId?: string, creativeId?: string, placementId?: string) => boolean;
isAdmin: boolean;
// Check if user is admin in a specific workspace
isWorkspaceAdmin: (workspaceId: string) => Promise;
@@ -141,7 +141,7 @@ export const WorkspaceProvider: React.FC = ({
const isAdmin = permissions.some((p) => p.key === "admin_full");
const hasPermission = useCallback(
- (key: PermissionKey, projectId?: string): boolean => {
+ (key: PermissionKey, projectId?: string, creativeId?: string, placementId?: string): boolean => {
// Admin has all permissions
if (isAdmin) return true;
@@ -154,13 +154,30 @@ export const WorkspaceProvider: React.FC = ({
// If projectId provided, check if it's in scopes
if (projectId) {
- return permission.scopes.some(
+ const hasProjectScope = permission.scopes.some(
(s) => s.type === "project" && s.id === projectId
);
+ if (hasProjectScope) return true;
}
- // Permission exists but is scoped - need projectId to verify
- return true;
+ // If creativeId provided, check if it's in scopes
+ if (creativeId) {
+ const hasCreativeScope = permission.scopes.some(
+ (s) => s.type === "creative" && s.id === creativeId
+ );
+ if (hasCreativeScope) return true;
+ }
+
+ // If placementId provided, check if it's in scopes
+ if (placementId) {
+ const hasPlacementScope = permission.scopes.some(
+ (s) => s.type === "placement" && s.id === placementId
+ );
+ if (hasPlacementScope) return true;
+ }
+
+ // Permission exists but is scoped and no matching scope found
+ return false;
},
[permissions, isAdmin]
);
diff --git a/lib/types/api.ts b/lib/types/api.ts
index 623b0b4..12f1cbc 100644
--- a/lib/types/api.ts
+++ b/lib/types/api.ts
@@ -64,11 +64,15 @@ export type PermissionKey =
| "admin_full"
| "projects_read"
| "projects_write"
+ | "creatives_read"
+ | "creatives_write"
| "placements_read"
| "placements_write"
- | "analytics_read";
+ | "analytics_read"
+ | "analytics_without_clicks"
+ | "analytics_own_creatives";
-export type PermissionScopeType = "project";
+export type PermissionScopeType = "project" | "creative" | "placement";
export interface PermissionScope {
type: PermissionScopeType;
@@ -80,6 +84,32 @@ export interface Permission {
scopes?: PermissionScope[];
}
+export const PERMISSION_LABELS: Record = {
+ admin_full: "Полный доступ",
+ projects_read: "Просмотр проектов",
+ projects_write: "Редактирование проектов",
+ creatives_read: "Просмотр креативов",
+ creatives_write: "Создание/редактирование креативов",
+ placements_read: "Просмотр планов закупок",
+ placements_write: "Создание/редактирование закупок",
+ analytics_read: "Полный доступ к статистике",
+ analytics_without_clicks: "Статистика без переходов",
+ analytics_own_creatives: "Статистика только своих креативов",
+};
+
+export const ALL_PERMISSIONS: PermissionKey[] = [
+ "admin_full",
+ "projects_read",
+ "projects_write",
+ "creatives_read",
+ "creatives_write",
+ "placements_read",
+ "placements_write",
+ "analytics_read",
+ "analytics_without_clicks",
+ "analytics_own_creatives",
+];
+
export interface WorkspaceMemberUser {
id: string; // user_id
telegram_id: number;