feat: update analytics
This commit is contained in:
@@ -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 }
|
||||
),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
117
lib/types/api.ts
117
lib/types/api.ts
@@ -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
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user