717 lines
19 KiB
TypeScript
717 lines
19 KiB
TypeScript
// ============================================================================
|
||
// API Types - Based on actual API responses
|
||
// ============================================================================
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// User & Auth
|
||
// ----------------------------------------------------------------------------
|
||
|
||
export interface User {
|
||
id: string; // UUID
|
||
telegram_id: number;
|
||
username: string | null;
|
||
first_name: string | null;
|
||
last_name: string | null;
|
||
}
|
||
|
||
export interface AuthCompleteResponse {
|
||
access_token: string;
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Pagination
|
||
// ----------------------------------------------------------------------------
|
||
|
||
export interface PaginatedResponse<T> {
|
||
items: T[];
|
||
total: number;
|
||
page: number;
|
||
size: number;
|
||
pages: number;
|
||
}
|
||
|
||
export interface PaginationParams {
|
||
page?: number;
|
||
size?: number;
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Workspaces
|
||
// ----------------------------------------------------------------------------
|
||
|
||
export interface Workspace {
|
||
id: string; // UUID
|
||
name: string;
|
||
avatar_url?: string | null;
|
||
}
|
||
|
||
export interface WorkspaceCreateRequest {
|
||
name: string;
|
||
avatar_file?: File | null;
|
||
}
|
||
|
||
export interface WorkspaceUpdateRequest {
|
||
name?: string;
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Workspace Members & Permissions
|
||
// ----------------------------------------------------------------------------
|
||
|
||
export type WorkspaceUserStatus = "active" | "invited";
|
||
|
||
export type PermissionKey =
|
||
| "admin_full"
|
||
| "projects_read"
|
||
| "projects_write"
|
||
| "creatives_read"
|
||
| "creatives_write"
|
||
| "placements_read"
|
||
| "placements_write"
|
||
| "analytics_read"
|
||
| "analytics_without_clicks"
|
||
| "analytics_own_creatives";
|
||
|
||
export type PermissionScopeType = "project" | "creative" | "placement";
|
||
|
||
export interface PermissionScope {
|
||
type: PermissionScopeType;
|
||
id: string; // UUID
|
||
}
|
||
|
||
export interface Permission {
|
||
key: PermissionKey;
|
||
scopes?: PermissionScope[];
|
||
}
|
||
|
||
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 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;
|
||
username: string | null;
|
||
first_name: string | null;
|
||
last_name: string | null;
|
||
}
|
||
|
||
export interface WorkspaceMember {
|
||
id: string; // member record id
|
||
status: WorkspaceUserStatus;
|
||
user: WorkspaceMemberUser;
|
||
permissions: Permission[];
|
||
}
|
||
|
||
export interface UpdatePermissionsRequest {
|
||
permissions: Permission[];
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Workspace Invites
|
||
// ----------------------------------------------------------------------------
|
||
|
||
export type WorkspaceInviteStatus = "pending" | "accepted" | "rejected";
|
||
|
||
export interface WorkspaceInvite {
|
||
id: string; // UUID
|
||
username: string;
|
||
status: WorkspaceInviteStatus;
|
||
created_at: string;
|
||
}
|
||
|
||
export interface WorkspaceInviteCreateRequest {
|
||
username: string; // без @
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Projects (Target Channels)
|
||
// ----------------------------------------------------------------------------
|
||
|
||
export type ProjectStatus = "active" | "inactive" | "archived";
|
||
|
||
export interface Project {
|
||
id: string; // UUID
|
||
telegram_id: number;
|
||
title: string;
|
||
username: string | null;
|
||
status: ProjectStatus;
|
||
subscribers_count?: number;
|
||
purchase_invite_type_default?: "public" | "approval";
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Channels (Catalog for Placements)
|
||
// ----------------------------------------------------------------------------
|
||
|
||
export type ChannelStatus = "active" | "inactive" | "archived";
|
||
|
||
export interface Channel {
|
||
id: string; // UUID
|
||
telegram_id: number;
|
||
title: string;
|
||
username: string | null;
|
||
description?: string | null;
|
||
subscribers_count?: number;
|
||
}
|
||
|
||
export interface ChannelsQueryParams extends PaginationParams {
|
||
username?: string;
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Purchase Plan
|
||
// ----------------------------------------------------------------------------
|
||
|
||
export type PurchasePlanStatus = "planned" | "completed" | "cancelled";
|
||
|
||
export interface PurchasePlanChannel {
|
||
id: string; // UUID
|
||
status: PurchasePlanStatus;
|
||
planned_cost: number | null;
|
||
comment: string | null;
|
||
channel: {
|
||
id: string;
|
||
telegram_id: number;
|
||
title: string;
|
||
username: string | null;
|
||
};
|
||
}
|
||
|
||
export interface PurchasePlanChannelCreateRequest {
|
||
channel_id: string;
|
||
planned_cost?: number;
|
||
comment?: string;
|
||
}
|
||
|
||
export interface PurchasePlanChannelUpdateRequest {
|
||
planned_cost?: number;
|
||
comment?: string;
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Creatives
|
||
// ----------------------------------------------------------------------------
|
||
|
||
export type CreativeStatus = "active" | "archived";
|
||
|
||
export interface CreativeMediaItem {
|
||
media_type: string;
|
||
media_file_id: string;
|
||
position: number;
|
||
}
|
||
|
||
export interface CreativeButton {
|
||
text: string;
|
||
url: string;
|
||
}
|
||
|
||
export interface Creative {
|
||
id: string; // UUID
|
||
name: string;
|
||
text: string; // Contains {invite_link} placeholder
|
||
media_items: CreativeMediaItem[];
|
||
buttons: CreativeButton[];
|
||
project_id: string;
|
||
project_channel_title: string;
|
||
created_at: string;
|
||
status: CreativeStatus;
|
||
placements_count: number;
|
||
}
|
||
|
||
export interface CreativeCreateRequest {
|
||
name: string;
|
||
text: string;
|
||
}
|
||
|
||
export interface CreativeUpdateRequest {
|
||
name?: string;
|
||
text?: string;
|
||
status?: CreativeStatus;
|
||
media_items?: CreativeMediaItem[];
|
||
buttons?: CreativeButton[];
|
||
}
|
||
|
||
export interface CreativesQueryParams extends PaginationParams {
|
||
project_id?: string;
|
||
include_archived?: boolean;
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Placements
|
||
// ----------------------------------------------------------------------------
|
||
|
||
export type PlacementStatus =
|
||
| "Без статуса"
|
||
| "Написать"
|
||
| "Ждём ответа"
|
||
| "Согласование условий"
|
||
| "Оплатить"
|
||
| "Оплачено"
|
||
| "Отмена"
|
||
| "Не подходит цена"
|
||
| "Неактуально"
|
||
| "Не отвечает";
|
||
|
||
export type PlacementPostStatus =
|
||
| "Без статуса"
|
||
| "Отправить пост"
|
||
| "Согласование поста"
|
||
| "Ожидание отложки"
|
||
| "Запланирован"
|
||
| "Пост вышел"
|
||
| "Размещение отработало - пост удалён"
|
||
| "Размещение отработало - пост не удалён"
|
||
| "Проверить - пост удалён раньше срока"
|
||
| "Проверить - пост не вышел"
|
||
| "Размещение отработало";
|
||
|
||
export type InviteLinkType = "public" | "approval";
|
||
export type PlacementType = "self_promo" | "standard";
|
||
export type CostType = "fixed" | "cpm";
|
||
export type PostViewsAvailability =
|
||
| "unknown"
|
||
| "available"
|
||
| "unavailable"
|
||
| "manual";
|
||
|
||
export interface PlacementChannel {
|
||
id: string;
|
||
telegram_id: number;
|
||
title: string;
|
||
username: string | null;
|
||
subscribers_count?: number;
|
||
}
|
||
|
||
export interface PlacementCostInfo {
|
||
type: CostType;
|
||
value: number;
|
||
}
|
||
|
||
export interface PlacementDetails {
|
||
placement_at: string | null; // ISO 8601
|
||
payment_at: string | null; // ISO 8601
|
||
cost: PlacementCostInfo | null;
|
||
cost_before_bargain: PlacementCostInfo | null;
|
||
placement_type: PlacementType | null;
|
||
format: string | null;
|
||
comment: string | null;
|
||
creative_id: string | null;
|
||
}
|
||
|
||
export interface PlacementPost {
|
||
subscriptions_count: number;
|
||
views_count: number | null;
|
||
created_at: string;
|
||
post: {
|
||
id: string;
|
||
message_id: number;
|
||
text: string;
|
||
url: string | null;
|
||
deleted_from_channel_at: string | null;
|
||
published_at: string | null;
|
||
created_at: string;
|
||
updated_at: string;
|
||
} | null;
|
||
}
|
||
|
||
export interface PlacementPostWithStatus extends PlacementPost {
|
||
status: PlacementPostStatus;
|
||
time_on_top: number | null;
|
||
}
|
||
|
||
export interface Placement {
|
||
id: string;
|
||
status: PlacementStatus;
|
||
creative_id: string | null;
|
||
creative_name: string | null;
|
||
comment: string | null;
|
||
invite_link: string | null;
|
||
invite_link_created_at: string | null;
|
||
invite_link_type: InviteLinkType;
|
||
channel: PlacementChannel;
|
||
project: Project | null;
|
||
short_id: string;
|
||
details: PlacementDetails | null;
|
||
placement_post: PlacementPostWithStatus | null;
|
||
created_at: string;
|
||
}
|
||
|
||
export interface PlacementWithStats extends Placement {
|
||
cpf: number | null;
|
||
cpm: number | null;
|
||
time_in_feed?: number | null;
|
||
conversion_24h?: number | null;
|
||
conversion_48h?: number | null;
|
||
conversion_total?: number | null;
|
||
total_unsubs?: number | null;
|
||
unsub_percent?: number | null;
|
||
total_active?: number | null;
|
||
}
|
||
|
||
export interface PlacementsGroupedByChannel {
|
||
channel: PlacementChannel;
|
||
placements: PlacementWithStats[];
|
||
total_cost: number;
|
||
total_subscriptions: number;
|
||
total_views: number;
|
||
}
|
||
|
||
export interface ProjectPlacementsSummary {
|
||
total_placements: number;
|
||
total_channels: number;
|
||
total_cost: number;
|
||
total_subscriptions: number;
|
||
total_views: number;
|
||
avg_cpf: number | null;
|
||
avg_cpm: number | null;
|
||
}
|
||
|
||
export interface PlacementCreateRequest {
|
||
project_id: string;
|
||
placement_channel_id: string;
|
||
creative_id: string;
|
||
placement_date: string; // ISO 8601
|
||
cost?: number;
|
||
comment?: string;
|
||
ad_post_url?: string;
|
||
invite_link_type: InviteLinkType;
|
||
}
|
||
|
||
export interface PlacementUpdateRequest {
|
||
placement_date?: string;
|
||
cost?: number;
|
||
comment?: string;
|
||
ad_post_url?: string;
|
||
}
|
||
|
||
export interface UpdatePlacementInProjectRequest {
|
||
status?: string;
|
||
comment?: string | null;
|
||
creative_id?: string | null;
|
||
creative_name?: string | null;
|
||
placement_at?: string | null;
|
||
payment_at?: string | null;
|
||
cost?: { type: CostType; value: number } | null;
|
||
cost_before_bargain?: { type: CostType; value: number } | null;
|
||
placement_type?: PlacementType | null;
|
||
format?: string | null;
|
||
}
|
||
|
||
export interface PlacementsQueryParams extends PaginationParams {
|
||
project_id?: string;
|
||
placement_channel_id?: string;
|
||
creative_id?: string;
|
||
include_archived?: boolean;
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Views History
|
||
// ----------------------------------------------------------------------------
|
||
|
||
export interface ViewsHistoryItem {
|
||
id: string; // UUID
|
||
views_count: number;
|
||
fetched_at: string; // ISO 8601
|
||
}
|
||
|
||
export interface ViewsHistoryQueryParams extends PaginationParams {
|
||
from_date?: string;
|
||
to_date?: string;
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Analytics
|
||
// ----------------------------------------------------------------------------
|
||
|
||
export type DateGrouping = "day" | "week" | "month" | "DAY" | "WEEK" | "MONTH";
|
||
|
||
// Placements Analytics (detailed view with all fields)
|
||
export interface PlacementAnalyticsItem {
|
||
id: string;
|
||
project_id: string;
|
||
project_title: string;
|
||
channel_id: string;
|
||
channel_title: string;
|
||
creative_id: string | null;
|
||
creative_name: string | null;
|
||
cost: number | null;
|
||
cost_type: CostType;
|
||
cost_before_bargain: number | null;
|
||
payment_at: string | null;
|
||
placement_type: PlacementType | null;
|
||
comment: string | null;
|
||
format: string | null;
|
||
invite_link_type: InviteLinkType | null;
|
||
placement_date: string | null;
|
||
subscriptions_count: number;
|
||
views_count: number | null;
|
||
cpf: number | null;
|
||
cpm: number | null;
|
||
time_on_top: number | null;
|
||
time_in_feed: number | null;
|
||
invite_link: string | null;
|
||
invite_link_created_at: string | null;
|
||
post_url: string | null;
|
||
post_deleted_at: string | null;
|
||
conversion_24h: number | null;
|
||
conversion_48h: number | null;
|
||
conversion_total: number | null;
|
||
unsubscriptions_count: number;
|
||
unsub_percent: number | null;
|
||
total_active: number;
|
||
}
|
||
|
||
export interface PlacementsAnalyticsQueryParams extends PaginationParams {
|
||
project_ids?: string;
|
||
status_list?: string;
|
||
placement_channel_ids?: string;
|
||
creative_ids?: string;
|
||
cost_types?: string;
|
||
placement_types?: string;
|
||
invite_link_types?: string;
|
||
cost_min?: number;
|
||
cost_max?: number;
|
||
views_min?: number;
|
||
views_max?: number;
|
||
subscriptions_min?: number;
|
||
subscriptions_max?: number;
|
||
cpm_min?: number;
|
||
cpm_max?: number;
|
||
channel_title_contains?: string;
|
||
creative_name_contains?: string;
|
||
comment_contains?: string;
|
||
placement_date_from?: string;
|
||
placement_date_to?: string;
|
||
sort_by?: string;
|
||
sort_direction?: "asc" | "desc";
|
||
}
|
||
|
||
// Creatives Analytics (matches actual API response)
|
||
export interface CreativeAnalyticsItem {
|
||
id: string;
|
||
name: string;
|
||
placements_count: number;
|
||
total_cost: number;
|
||
total_subscriptions: number;
|
||
total_views: number;
|
||
avg_cpf: number | null; // cost per follower (same as CPS)
|
||
avg_cpm: number | null;
|
||
}
|
||
|
||
// Channels Analytics
|
||
export interface ChannelAnalyticsItem {
|
||
channel_id: string;
|
||
channel_title: string;
|
||
channel_username: string | null;
|
||
placements_count: number;
|
||
total_cost: number;
|
||
total_subscriptions: number;
|
||
total_views: number;
|
||
avg_cps: number | null;
|
||
avg_cpm: number | null;
|
||
}
|
||
|
||
// Spending Analytics
|
||
export interface SpendingDataPoint {
|
||
period: string;
|
||
cost: number;
|
||
subscriptions: number;
|
||
views: number;
|
||
cpf: number | null;
|
||
cpm: number | null;
|
||
}
|
||
|
||
export interface SpendingAnalytics {
|
||
chart_data: SpendingDataPoint[];
|
||
total_cost: number;
|
||
total_subscriptions: number;
|
||
total_views: number;
|
||
avg_cpf: number | null;
|
||
avg_cpm: number | null;
|
||
placements_count: number;
|
||
}
|
||
|
||
export interface AnalyticsQueryParams extends PaginationParams {
|
||
project_id?: string;
|
||
}
|
||
|
||
// Projects Analytics Chart API
|
||
export interface ProjectsAnalyticsMetrics {
|
||
total_cost: number;
|
||
purchases_count: number;
|
||
total_subscriptions: number;
|
||
total_views: number;
|
||
avg_cpf: number | null;
|
||
avg_cpm: number | null;
|
||
avg_post_cost: number | null;
|
||
clicks_count: number;
|
||
reach_volume: number;
|
||
total_discounts: number | null;
|
||
avg_discount_percent: number | null;
|
||
avg_conversion: number | null;
|
||
}
|
||
|
||
export interface ProjectsAnalyticsPeriod {
|
||
period: string;
|
||
period_label: string;
|
||
metrics: ProjectsAnalyticsMetrics;
|
||
}
|
||
|
||
export interface ProjectsAnalyticsResponse {
|
||
periods: ProjectsAnalyticsPeriod[];
|
||
totals: ProjectsAnalyticsMetrics;
|
||
}
|
||
|
||
export interface ProjectsAnalyticsQueryParams {
|
||
project_ids?: string[];
|
||
date_from?: string;
|
||
date_to?: string;
|
||
grouping: "day" | "week" | "month" | "quarter";
|
||
date_grouping: "purchase_date" | "link_date" | "placement_date";
|
||
metrics?: string[];
|
||
}
|
||
|
||
export interface SpendingAnalyticsQueryParams {
|
||
project_id?: string;
|
||
date_from?: string;
|
||
date_to?: string;
|
||
grouping?: DateGrouping;
|
||
}
|
||
|
||
// Overview Analytics
|
||
export interface OverviewMetric {
|
||
value: number;
|
||
delta_percent: number;
|
||
}
|
||
|
||
export interface OverviewDailyStat {
|
||
date: string; // YYYY-MM-DD
|
||
cost: number;
|
||
subscriptions: number;
|
||
subscriptions_delta: number;
|
||
cpf: number;
|
||
}
|
||
|
||
export interface OverviewChannelCpf {
|
||
channel_id: string;
|
||
title: string;
|
||
username: string | null;
|
||
cpf: number;
|
||
total_cost: number;
|
||
subscriptions: number;
|
||
}
|
||
|
||
export interface OverviewProjectSpending {
|
||
project_id: string;
|
||
project_title: string;
|
||
project_username: string | null;
|
||
total_cost: number;
|
||
}
|
||
|
||
export interface OverviewAnalytics {
|
||
total_cost: OverviewMetric;
|
||
total_reach: OverviewMetric;
|
||
placements_count: OverviewMetric;
|
||
subscriptions_count: OverviewMetric;
|
||
avg_cpm: OverviewMetric;
|
||
avg_cpf: OverviewMetric;
|
||
daily_stats: OverviewDailyStat[];
|
||
top_channels_by_cpf: OverviewChannelCpf[];
|
||
worst_channels_by_cpf: OverviewChannelCpf[];
|
||
project_spending: OverviewProjectSpending[];
|
||
}
|
||
|
||
export interface OverviewAnalyticsQueryParams {
|
||
project_id?: string;
|
||
date_from?: string;
|
||
date_to?: string;
|
||
}
|
||
|
||
// Placements Detail Analytics
|
||
export interface PlacementAnalyticsDetail {
|
||
id: string;
|
||
project_id: string;
|
||
project_title: string;
|
||
channel_id: string;
|
||
channel_title: string;
|
||
creative_id: string | null;
|
||
creative_name: string | null;
|
||
cost: number | null;
|
||
cost_type: CostType;
|
||
cost_before_bargain: number | null;
|
||
payment_at: string | null;
|
||
placement_type: PlacementType | null;
|
||
comment: string | null;
|
||
format: string | null;
|
||
invite_link_type: InviteLinkType | null;
|
||
placement_date: string | null;
|
||
subscriptions_count: number;
|
||
views_count: number | null;
|
||
cpf: number | null;
|
||
cpm: number | null;
|
||
time_on_top: number | null;
|
||
time_in_feed: number | null;
|
||
invite_link: string | null;
|
||
invite_link_created_at: string | null;
|
||
post_url: string | null;
|
||
post_deleted_at: string | null;
|
||
conversion_24h: number | null;
|
||
conversion_48h: number | null;
|
||
conversion_total: number | null;
|
||
unsubscriptions_count: number;
|
||
unsub_percent: number | null;
|
||
total_active: number;
|
||
}
|
||
|
||
export interface PlacementsDetailAnalyticsQueryParams extends PaginationParams {
|
||
project_id?: string;
|
||
channel_id?: string;
|
||
status?: string;
|
||
sort_by?: string;
|
||
sort_direction?: "asc" | "desc";
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Common Types
|
||
// ----------------------------------------------------------------------------
|
||
|
||
export interface ApiError {
|
||
detail: string;
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Legacy Aliases (for backwards compatibility during migration)
|
||
// ----------------------------------------------------------------------------
|
||
|
||
/** @deprecated Use Project instead */
|
||
export type TargetChannel = Project;
|
||
|
||
/** @deprecated Use Channel instead */
|
||
export type ExternalChannel = Channel;
|
||
|
||
/** @deprecated Use PlacementCreateRequest with new fields */
|
||
export type PurchaseCreateRequest = PlacementCreateRequest;
|