Files
portal/app/resources/js/api/adminDashboard.ts
T
Дмитрий 93e8393014 feat(балансы-fe): плитка «Балансы сервисов» + drill + кнопки «Пополнить»
- 5-я плитка дашборда со светофором (worst-of сервисов, поддержка grey=нет данных)
- Drill-таблица: Сервис · Баланс · Хватит на N дней · Статус · кнопка «Пополнить»
- Кнопка «Пополнить» (target=_blank) → страница оплаты сервиса; YC — прямо на биллинг
- Клиент getDashboardBalances + типы; Vitest 12/12 (тайл, drill, href кнопки)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 07:12:45 +03:00

154 lines
4.1 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;
};
balances: {
light: Light | 'grey';
count: number;
red: number;
};
}
export interface BalancesDetail {
light: Light | 'grey';
services: Array<{
service_key: string;
balance_amount: string | null;
currency: string;
daily_spend_estimate: string | null;
days_left: number | null;
light: Light | 'grey';
ok: boolean;
error: string | null;
checked_at: string | null;
topup_url: 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;
}
export async function getDashboardBalances(): Promise<BalancesDetail> {
const { data } = await apiClient.get<BalancesDetail>('/api/admin/dashboard/balances');
return data;
}