feat: update analytics

This commit is contained in:
ivannoskov
2026-02-03 18:55:09 +03:00
parent d87d6e2568
commit e27a171c63
8 changed files with 1529 additions and 461 deletions

View File

@@ -10,7 +10,6 @@ import type {
SpendingAnalytics,
AnalyticsQueryParams,
SpendingAnalyticsQueryParams,
PaginatedResponse,
OverviewAnalytics,
OverviewAnalyticsQueryParams,
ProjectsAnalyticsResponse,
@@ -20,19 +19,22 @@ import type {
export const analyticsApi = {
/**
* Get placements analytics
* Get placements analytics (paginated with all fields)
*/
placements: (workspaceId: string, params?: PlacementsAnalyticsQueryParams) =>
api.get<PaginatedResponse<PlacementAnalyticsItem>>(
`/workspaces/${workspaceId}/analytics/placements`,
{ params }
),
api.get<{
items: PlacementAnalyticsItem[];
total: number;
page: number;
size: number;
pages: number;
}>(`/workspaces/${workspaceId}/analytics/placements`, { params }),
/**
* Get creatives analytics
*/
creatives: (workspaceId: string, params?: AnalyticsQueryParams) =>
api.get<PaginatedResponse<CreativeAnalyticsItem>>(
api.get<{ items: CreativeAnalyticsItem[]; total: number; page: number; size: number; pages: number }>(
`/workspaces/${workspaceId}/analytics/creatives`,
{ params }
),
@@ -41,7 +43,7 @@ export const analyticsApi = {
* Get channels analytics
*/
channels: (workspaceId: string, params?: AnalyticsQueryParams) =>
api.get<PaginatedResponse<ChannelAnalyticsItem>>(
api.get<{ items: ChannelAnalyticsItem[]; total: number; page: number; size: number; pages: number }>(
`/workspaces/${workspaceId}/analytics/channels`,
{ params }
),

View File

@@ -29,7 +29,7 @@ export const ALL_COLUMNS: ColumnConfig[] = [
// Content
{ id: "creative", label: "Креатив", group: "content", editable: true },
{ id: "invite_link", label: "Пригласительная ссылка", group: "content", editable: false },
{ id: "invite_link", label: "Пригл. ссылка", group: "content", editable: false },
{ id: "post_link", label: "Ссылка на пост", group: "content", editable: false },
// Finance

View File

@@ -396,9 +396,10 @@ export const demoAwareAnalyticsApi = {
placements: async (
workspaceId: string,
params?: {
project_id?: string;
placement_channel_id?: string;
creative_id?: string;
project_ids?: string;
status_list?: string;
placement_channel_ids?: string;
creative_ids?: string;
date_from?: string;
date_to?: string;
page?: number;
@@ -407,22 +408,22 @@ export const demoAwareAnalyticsApi = {
): Promise<PaginatedResponse<PlacementAnalyticsItem>> => {
if (isDemoWorkspace(workspaceId)) {
let items = demoPlacementAnalytics;
if (params?.project_id) {
items = items.filter((p) => p.project_id === params.project_id);
if (params?.project_ids) {
items = items.filter((p) => p.project_id === params.project_ids);
}
if (params?.placement_channel_id) {
items = items.filter((p) => p.placement_channel_id === params.placement_channel_id);
if (params?.placement_channel_ids) {
items = items.filter((p) => params.placement_channel_ids!.includes(p.channel_id));
}
if (params?.creative_id) {
items = items.filter((p) => p.creative_id === params.creative_id);
if (params?.creative_ids) {
items = items.filter((p) => p.creative_id && params.creative_ids!.includes(p.creative_id));
}
if (params?.date_from) {
const from = new Date(params.date_from);
items = items.filter((p) => new Date(p.placement_date) >= from);
items = items.filter((p) => p.placement_date && new Date(p.placement_date) >= from);
}
if (params?.date_to) {
const to = new Date(params.date_to);
items = items.filter((p) => new Date(p.placement_date) <= to);
items = items.filter((p) => p.placement_date && new Date(p.placement_date) <= to);
}
return paginate(items, params?.page, params?.size);
}
@@ -452,12 +453,12 @@ export const demoAwareAnalyticsApi = {
if (params.date_from) {
const from = new Date(params.date_from);
filtered = filtered.filter((p) => new Date(p.placement_date) >= from);
filtered = filtered.filter((p) => p.placement_date && new Date(p.placement_date) >= from);
}
if (params.date_to) {
const to = new Date(params.date_to);
filtered = filtered.filter((p) => new Date(p.placement_date) <= to);
filtered = filtered.filter((p) => p.placement_date && new Date(p.placement_date) <= to);
}
// Helper to format period label
@@ -483,6 +484,7 @@ export const demoAwareAnalyticsApi = {
// Group by period
const grouped: Record<string, typeof filtered> = {};
filtered.forEach((item) => {
if (!item.placement_date) return;
const date = new Date(item.placement_date);
let periodKey: string;

View File

@@ -766,87 +766,177 @@ export const demoChannelAnalytics: ChannelAnalyticsItem[] = [
},
];
// Placements Analytics (Projects Analytics)
// Placements Analytics (detailed view)
export const demoPlacementAnalytics: PlacementAnalyticsItem[] = [
{
id: "plac-0001-0000-0000-000000000001",
project_id: "proj-0001-0000-0000-000000000001",
project_channel_title: "Крипто Новости",
placement_channel_id: "chan-0001-0000-0000-000000000001",
placement_channel_title: "Технологии Будущего",
project_title: "Крипто Новости",
channel_id: "chan-0001-0000-0000-000000000001",
channel_title: "Технологии Будущего",
creative_id: "crea-0001-0000-0000-000000000001",
creative_name: "Крипто - Основной",
placement_date: "2024-12-20T12:00:00Z",
cost: 5500,
cost_type: "fixed",
cost_before_bargain: 6000,
payment_at: "2024-12-19T10:00:00Z",
placement_type: "standard",
comment: null,
format: null,
invite_link_type: "public",
placement_date: "2024-12-20T12:00:00Z",
subscriptions_count: 156,
views_count: 8500,
cpf: 35.26,
cpm: 647.06,
time_on_top: 60,
time_in_feed: null,
invite_link: "https://t.me/+test1",
invite_link_created_at: "2024-12-20T11:55:00Z",
post_url: "https://t.me/tech_future/1001",
post_deleted_at: null,
conversion_24h: 1.5,
conversion_48h: 2.1,
conversion_total: 1.84,
unsubscriptions_count: 5,
unsub_percent: 3.11,
total_active: 151,
},
{
id: "plac-0002-0000-0000-000000000002",
project_id: "proj-0001-0000-0000-000000000001",
project_channel_title: "Крипто Новости",
placement_channel_id: "chan-0002-0000-0000-000000000002",
placement_channel_title: "Инвестиции Pro",
project_title: "Крипто Новости",
channel_id: "chan-0002-0000-0000-000000000002",
channel_title: "Инвестиции Pro",
creative_id: "crea-0001-0000-0000-000000000001",
creative_name: "Крипто - Основной",
placement_date: "2024-12-18T14:00:00Z",
cost: 3200,
cost_type: "fixed",
cost_before_bargain: 3500,
payment_at: "2024-12-17T14:00:00Z",
placement_type: "standard",
comment: null,
format: null,
invite_link_type: "public",
placement_date: "2024-12-18T14:00:00Z",
subscriptions_count: 89,
views_count: 4200,
cpf: 35.96,
cpm: 761.9,
time_on_top: 45,
time_in_feed: null,
invite_link: "https://t.me/+test2",
invite_link_created_at: "2024-12-18T13:50:00Z",
post_url: "https://t.me/invest_pro/502",
post_deleted_at: null,
conversion_24h: 1.8,
conversion_48h: 2.2,
conversion_total: 2.12,
unsubscriptions_count: 3,
unsub_percent: 3.26,
total_active: 86,
},
{
id: "plac-0003-0000-0000-000000000003",
project_id: "proj-0001-0000-0000-000000000001",
project_channel_title: "Крипто Новости",
placement_channel_id: "chan-0003-0000-0000-000000000003",
placement_channel_title: "Программисты",
project_title: "Крипто Новости",
channel_id: "chan-0003-0000-0000-000000000003",
channel_title: "Программисты",
creative_id: "crea-0001-0000-0000-000000000001",
creative_name: "Крипто - Основной",
placement_date: "2024-12-15T10:00:00Z",
cost: 4800,
cost_type: "cpm",
cost_before_bargain: null,
payment_at: "2024-12-14T10:00:00Z",
placement_type: "standard",
comment: "Хороший канал",
format: null,
invite_link_type: "approval",
placement_date: "2024-12-15T10:00:00Z",
subscriptions_count: 234,
views_count: 12000,
cpf: 20.51,
cpm: 400.0,
time_on_top: 120,
time_in_feed: null,
invite_link: "https://t.me/+test3",
invite_link_created_at: "2024-12-15T09:55:00Z",
post_url: "https://t.me/programmers_hub/2050",
post_deleted_at: null,
conversion_24h: 1.2,
conversion_48h: 1.8,
conversion_total: 1.95,
unsubscriptions_count: 8,
unsub_percent: 3.31,
total_active: 226,
},
{
id: "plac-0004-0000-0000-000000000004",
project_id: "proj-0002-0000-0000-000000000002",
project_channel_title: "IT Вакансии",
placement_channel_id: "chan-0004-0000-0000-000000000004",
placement_channel_title: "Маркетинг Pro",
project_title: "IT Вакансии",
channel_id: "chan-0004-0000-0000-000000000004",
channel_title: "Маркетинг Pro",
creative_id: "crea-0002-0000-0000-000000000002",
creative_name: "Крипто - Агрессивный",
placement_date: "2024-12-10T16:00:00Z",
cost: 2500,
cost_type: "fixed",
cost_before_bargain: 3000,
payment_at: "2024-12-09T16:00:00Z",
placement_type: "self_promo",
comment: null,
format: null,
invite_link_type: "public",
placement_date: "2024-12-10T16:00:00Z",
subscriptions_count: 67,
views_count: 3100,
cpf: 37.31,
cpm: 806.45,
time_on_top: 30,
time_in_feed: null,
invite_link: "https://t.me/+test4",
invite_link_created_at: "2024-12-10T15:55:00Z",
post_url: "https://t.me/marketing_pro/803",
post_deleted_at: "2024-12-12T16:00:00Z",
conversion_24h: 1.5,
conversion_48h: 2.0,
conversion_total: 2.16,
unsubscriptions_count: 2,
unsub_percent: 2.9,
total_active: 65,
},
{
id: "plac-0005-0000-0000-000000000005",
project_id: "proj-0002-0000-0000-000000000002",
project_channel_title: "IT Вакансии",
placement_channel_id: "chan-0005-0000-0000-000000000005",
placement_channel_title: "Финансовая грамотность",
project_title: "IT Вакансии",
channel_id: "chan-0005-0000-0000-000000000005",
channel_title: "Финансовая грамотность",
creative_id: "crea-0002-0000-0000-000000000002",
creative_name: "Крипто - Агрессивный",
placement_date: "2024-12-05T11:00:00Z",
cost: 6200,
cost_type: "fixed",
cost_before_bargain: 7500,
payment_at: "2024-12-04T11:00:00Z",
placement_type: "standard",
comment: null,
format: null,
invite_link_type: "approval",
placement_date: "2024-12-05T11:00:00Z",
subscriptions_count: 312,
views_count: 15600,
cpf: 19.87,
cpm: 397.44,
time_on_top: 90,
time_in_feed: null,
invite_link: "https://t.me/+test5",
invite_link_created_at: "2024-12-05T10:55:00Z",
post_url: "https://t.me/finance_edu/1204",
post_deleted_at: null,
conversion_24h: 1.3,
conversion_48h: 1.9,
conversion_total: 2.0,
unsubscriptions_count: 12,
unsub_percent: 3.7,
total_active: 300,
},
];

View File

@@ -448,30 +448,65 @@ export interface ViewsHistoryQueryParams extends PaginationParams {
export type DateGrouping = "day" | "week" | "month" | "DAY" | "WEEK" | "MONTH";
// Placements Analytics (Projects Analytics)
// Placements Analytics (detailed view with all fields)
export interface PlacementAnalyticsItem {
id: string; // UUID
project_id: string; // UUID
project_channel_title: string;
placement_channel_id: string; // UUID
placement_channel_title: string;
creative_id: string; // UUID
creative_name: string;
placement_date: string; // ISO 8601
cost: number;
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;
cpf: number;
cpm: number;
post_url?: string | null;
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_id?: string;
placement_channel_id?: string;
creative_id?: string;
date_from?: string;
date_to?: string;
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)
@@ -615,6 +650,50 @@ export interface OverviewAnalyticsQueryParams {
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
// ----------------------------------------------------------------------------