fix(analytics): rewrite funnel as real order path

- SQL admin_automation_economics: new columns based on actual order lifecycle
  - sms_sent: got at least 1 SMS
  - completed_auto: delivered/picked_up without manual_confirmation_at
  - completed_manual: delivered/picked_up with manual_confirmation_at
  - stuck_in_manual: manual_required + still waiting
  - no_sms_completed: imported as already delivered
- Funnel steps: Всего → SMS → Авто-завершено → Завершено вручную → Застряло → Без SMS → Хранение → Отменено
- Footer: Автоматизация % / Ручное вмешательство % / Завершено всего
- Fixes: old funnel counted manual_confirmation_required as "согласовано"
  and missed 31 completed orders (notification_status=completed vs confirmed)
This commit is contained in:
root 2026-07-06 13:16:44 +00:00
parent b0bc056090
commit a695a73af7
1 changed files with 14 additions and 11 deletions

View File

@ -185,14 +185,17 @@ export const AdminDashboard = () => {
total: d.total || 0, delivered: d.delivered || 0, picked_up: d.picked_up || 0, problems: d.problems || 0, total: d.total || 0, delivered: d.delivered || 0, picked_up: d.picked_up || 0, problems: d.problems || 0,
})); }));
// Funnel Data // Funnel: Real order path
// Each step shows how many orders reached that stage
const funnelSteps = [ const funnelSteps = [
{ label: 'Согласовано после 1-й SMS', value: econ.confirmed_after_sms1 || 0, color: '#22c55e' }, { label: 'Всего заказов', value: econ.total_groups || 0, color: '#94a3b8' },
{ label: 'Согласовано после 2-й SMS', value: econ.confirmed_after_sms2 || 0, color: '#14b8a6' }, { label: 'SMS отправлено', value: econ.sms_sent || 0, color: '#3b82f6' },
{ label: 'Согласовано вручную', value: econ.confirmed_via_manual || 0, color: '#eab308' }, { label: 'Авто-завершено', value: econ.completed_auto || 0, color: '#22c55e' },
{ label: 'Ручное назначение даты', value: econ.manual_date_set_count || 0, color: '#f97316' }, { label: 'Завершено вручную', value: econ.completed_manual || 0, color: '#14b8a6' },
{ label: 'Застряло в ручном', value: econ.stuck_in_manual || 0, color: '#eab308' },
{ label: 'Без SMS завершено', value: econ.no_sms_completed || 0, color: '#6366f1' },
{ label: 'Платное хранение', value: econ.paid_storage_count || 0, color: '#06b6d4' }, { label: 'Платное хранение', value: econ.paid_storage_count || 0, color: '#06b6d4' },
{ label: 'Отмена', value: econ.cancelled_count || 0, color: '#ef4444' }, { label: 'Отменено', value: econ.cancelled_count || 0, color: '#ef4444' },
]; ];
// Grid Layout // Grid Layout
@ -316,7 +319,7 @@ export const AdminDashboard = () => {
{/* Funnel — 4 cols desktop */} {/* Funnel — 4 cols desktop */}
<Panel style={{ padding: mobile ? '0.75rem' : '1.25rem', gridColumn: colSpan(4) }}> <Panel style={{ padding: mobile ? '0.75rem' : '1.25rem', gridColumn: colSpan(4) }}>
<SectionHeader title="Воронка согласования" subtitle={periodLabel} mobile={mobile} /> <SectionHeader title="Путь заказа" subtitle={periodLabel} mobile={mobile} />
{totalGroups === 0 ? ( {totalGroups === 0 ? (
<div style={{ color: 'var(--color-text-muted)', textAlign: 'center', padding: '1rem' }}>Нет данных</div> <div style={{ color: 'var(--color-text-muted)', textAlign: 'center', padding: '1rem' }}>Нет данных</div>
) : ( ) : (
@ -354,16 +357,16 @@ export const AdminDashboard = () => {
marginTop: '0.75rem', paddingTop: '0.6rem', borderTop: '1px solid var(--color-border)', marginTop: '0.75rem', paddingTop: '0.6rem', borderTop: '1px solid var(--color-border)',
}}> }}>
<div style={{ textAlign: 'center' }}> <div style={{ textAlign: 'center' }}>
<div style={{ fontSize: fontSize.xs, color: '#22c55e', marginBottom: '1px' }}>Автосогласование</div> <div style={{ fontSize: fontSize.xs, color: '#22c55e', marginBottom: '1px' }}>Автоматизация</div>
<div style={{ fontSize: mobile ? '0.95rem' : '1.1rem', fontWeight: 700, color: '#22c55e' }}>{econ.auto_confirm_pct ?? 0}%</div> <div style={{ fontSize: mobile ? '0.95rem' : '1.1rem', fontWeight: 700, color: '#22c55e' }}>{econ.automation_pct ?? 0}%</div>
</div> </div>
<div style={{ textAlign: 'center' }}> <div style={{ textAlign: 'center' }}>
<div style={{ fontSize: fontSize.xs, color: '#ef4444', marginBottom: '1px' }}>Ручное вмешательство</div> <div style={{ fontSize: fontSize.xs, color: '#ef4444', marginBottom: '1px' }}>Ручное вмешательство</div>
<div style={{ fontSize: mobile ? '0.95rem' : '1.1rem', fontWeight: 700, color: '#ef4444' }}>{econ.manual_intervention_pct ?? 0}%</div> <div style={{ fontSize: mobile ? '0.95rem' : '1.1rem', fontWeight: 700, color: '#ef4444' }}>{econ.manual_intervention_pct ?? 0}%</div>
</div> </div>
<div style={{ textAlign: 'center', display: mobile ? 'none' : 'block' }}> <div style={{ textAlign: 'center', display: mobile ? 'none' : 'block' }}>
<div style={{ fontSize: fontSize.xs, color: 'var(--color-text-muted)', marginBottom: '1px' }}>Всего согласовано</div> <div style={{ fontSize: fontSize.xs, color: 'var(--color-text-muted)', marginBottom: '1px' }}>Завершено всего</div>
<div style={{ fontSize: mobile ? '0.95rem' : '1.1rem', fontWeight: 700, color: 'var(--color-text)' }}>{econ.confirmed_auto_total ?? 0}</div> <div style={{ fontSize: mobile ? '0.95rem' : '1.1rem', fontWeight: 700, color: 'var(--color-text)' }}>{(econ.completed_auto || 0) + (econ.completed_manual || 0) + (econ.no_sms_completed || 0)}</div>
</div> </div>
</div> </div>
</div> </div>