f30c6612c0
По сверке прод-данных с реальностью (часть чисел вводила в заблуждение): - Финансы: +периоды 60 и 90 дней (крупные пополнения старше 30д теперь видны). - Здоровье: «инциденты» больше не считают авто-лог ошибок джоб (summary 'Автоматически:%') — раньше копилось 975 и держало красный ложно. Теперь: open_incidents = только реальные; добавлен job_errors_24h (повторяющиеся ошибки джоб за сутки) в подсистему queues. - Лиды: убраны обманчивый «% доставки» (это было «обработано», не доставлено) и «нераспределённые по менеджерам» (менеджеры не используются). Добавлено «получено от поставщика сегодня»; доставлено = реально созданные сегодня сделки. - Заказ: показаны дата снимка и полная картина (всего активных заказов / Σ лимита у поставщика) — сверка по снимку больше не выглядит занижено. Тесты: admin-срез 87 зелёных, unit 3/3, фронт 10/10. stan 0, pint/eslint/ type-check/build чисто. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
128 lines
3.5 KiB
TypeScript
128 lines
3.5 KiB
TypeScript
import { apiClient } from './client';
|
|
|
|
/**
|
|
* SaaS-admin «Командный центр» — типизированный клиент read-only агрегатов.
|
|
*
|
|
* Все 3 эндпоинта — GET под группой ['saas-admin','admin-db'] (cross-tenant
|
|
* через pgsql_admin). CSRF не нужен (только чтение).
|
|
* Backend: AdminDashboardController. Spec:
|
|
* docs/superpowers/specs/2026-06-27-admin-command-center-design.md
|
|
*/
|
|
|
|
export type Light = 'green' | 'amber' | 'red';
|
|
|
|
export interface DashboardSummary {
|
|
period: string;
|
|
finance: {
|
|
topups_rub: string;
|
|
charges_rub: string;
|
|
active_clients: number;
|
|
new_clients: number;
|
|
negative_balance_count: number;
|
|
light: Light;
|
|
};
|
|
health: {
|
|
light: Light;
|
|
open_incidents: number;
|
|
job_errors_24h: number;
|
|
failed_jobs_24h: number;
|
|
last_sync_status: string;
|
|
last_sync_at: string | null;
|
|
};
|
|
leads: {
|
|
light: Light;
|
|
delivered_today: number;
|
|
received_today: number;
|
|
stuck: number;
|
|
unrouted: number;
|
|
};
|
|
supply: {
|
|
light: Light;
|
|
demand: number;
|
|
formula: number;
|
|
ordered: number;
|
|
mismatches: number;
|
|
total_orders: number;
|
|
total_limit: number;
|
|
snapshot_date: string | null;
|
|
};
|
|
}
|
|
|
|
export interface LeadsDetail {
|
|
light: Light;
|
|
kpi: {
|
|
delivered_today: number;
|
|
received_today: number;
|
|
stuck: number;
|
|
unrouted: number;
|
|
};
|
|
}
|
|
|
|
export interface SupplyDetail {
|
|
snapshot_date: string | null;
|
|
light: Light;
|
|
totals: { demand: number; formula: number; ordered: number; mismatches: number };
|
|
total_orders: number;
|
|
total_limit: number;
|
|
groups: Array<{
|
|
signal_type: string;
|
|
identifier: string;
|
|
demand: number;
|
|
formula: number;
|
|
ordered: number;
|
|
in_sync: boolean;
|
|
}>;
|
|
}
|
|
|
|
export interface FinanceDetail {
|
|
period: string;
|
|
kpi: {
|
|
topups_rub: string;
|
|
charges_rub: string;
|
|
net_inflow_rub: string;
|
|
negative_balance_count: number;
|
|
};
|
|
attention: Array<{
|
|
id: number;
|
|
subdomain: string;
|
|
organization_name: string;
|
|
balance_rub: string;
|
|
state: string;
|
|
}>;
|
|
top_by_turnover: Array<{
|
|
id: number;
|
|
organization_name: string;
|
|
topped_rub: string;
|
|
}>;
|
|
}
|
|
|
|
export interface HealthDetail {
|
|
overall_light: Light;
|
|
subsystems: Array<{ key: string; light: Light; detail: string }>;
|
|
}
|
|
|
|
export async function getDashboardSummary(period: string): Promise<DashboardSummary> {
|
|
const { data } = await apiClient.get<DashboardSummary>('/api/admin/dashboard', { params: { period } });
|
|
return data;
|
|
}
|
|
|
|
export async function getDashboardFinance(period: string): Promise<FinanceDetail> {
|
|
const { data } = await apiClient.get<FinanceDetail>('/api/admin/dashboard/finance', { params: { period } });
|
|
return data;
|
|
}
|
|
|
|
export async function getDashboardHealth(): Promise<HealthDetail> {
|
|
const { data } = await apiClient.get<HealthDetail>('/api/admin/dashboard/health');
|
|
return data;
|
|
}
|
|
|
|
export async function getDashboardLeads(): Promise<LeadsDetail> {
|
|
const { data } = await apiClient.get<LeadsDetail>('/api/admin/dashboard/leads');
|
|
return data;
|
|
}
|
|
|
|
export async function getDashboardSupply(): Promise<SupplyDetail> {
|
|
const { data } = await apiClient.get<SupplyDetail>('/api/admin/dashboard/supply');
|
|
return data;
|
|
}
|