Files
portal/app/resources/js/api/adminVisitors.ts
T
Дмитрий b6b8d6e0d6 feat(visitors): раздел «Посетители» в админке — воронка, каналы, визиты, кабинет
Воронка подсвечивает красным шаг, где теряем больше всего людей. Гости с VPN
и хостингов показаны отдельным числом, а не подмешаны в конверсию. Город при
VPN честно помечается как недостоверный.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 05:03:38 +03:00

66 lines
1.9 KiB
TypeScript

import { apiClient } from './client';
/**
* SaaS-admin «Посетители» — свой счётчик (НЕ Метрика).
* Роботы сюда не попадают вовсе: они не исполняют JS страницы.
* Backend: AdminVisitorsController. Spec:
* docs/superpowers/specs/2026-07-13-visitors-analytics-design.md
*/
export type Period = 'today' | '7d' | '30d';
export interface Funnel {
period: string;
funnel: {
view: number;
alive: number;
cta_click: number;
register_open: number;
register_done: number;
login_done: number;
};
vpn_or_hosting: number;
}
export interface ChannelRow {
channel: string;
people: number;
cta_clicks: number;
registrations: number;
logins: number;
}
export interface VisitRow {
id: string;
first_seen_at: string;
channel: string;
device: string;
city: string | null;
asn_org: string | null;
is_datacenter: boolean;
is_human: boolean;
ip: string | null;
tenant_id: number | null;
steps: string[];
}
export interface PortalActivity {
screens: Array<{ screen: string; opens: number; people: number }>;
clients: Array<{ name: string; subdomain: string; last_seen_at: string }>;
}
export const getFunnel = (p: Period) =>
apiClient.get<Funnel>('/api/admin/visitors/funnel', { params: { period: p } }).then((r) => r.data);
export const getChannels = (p: Period) =>
apiClient
.get<{ channels: ChannelRow[] }>('/api/admin/visitors/channels', { params: { period: p } })
.then((r) => r.data.channels);
export const getVisits = (p: Period) =>
apiClient
.get<{ visits: VisitRow[] }>('/api/admin/visitors/visits', { params: { period: p } })
.then((r) => r.data.visits);
export const getPortalActivity = (p: Period) =>
apiClient.get<PortalActivity>('/api/admin/visitors/portal', { params: { period: p } }).then((r) => r.data);