Add Phase 3: Analytics & Forecast Intelligence

- Pipeline Movement Tracker with waterfall chart and snapshot capture
- Forecast Call Workspace with inline deal overrides and lock history
- Forecast Accuracy tracking against locked snapshots
- Win/Loss Analysis with rate breakdowns by stage, competitor, deal size, play
- Playbook Effectiveness dashboard with conversion funnel and step drop-off
- Account engagement trends with 12-week sparklines and direction badges
- Analytics sidebar navigation with collapsible submenu
- Data layer: forecast_locks, forecast_overrides tables, pipeline movement API
- Fix Turbopack function hoisting issues in Sidebar and db.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-01 13:49:05 -04:00
parent a131072226
commit d79794b164
11 changed files with 2646 additions and 30 deletions

View File

@@ -4,18 +4,6 @@ import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { useState } from 'react';
const navItems = [
{ href: '/', label: 'Executive Overview', icon: DashboardIcon, shortLabel: 'Overview' },
{ href: '/accounts', label: 'Accounts', icon: AccountsIcon, shortLabel: 'Accounts' },
{ href: '/activity', label: 'Activities', icon: ActivityIcon, shortLabel: 'Activities' },
{ href: '/pipeline', label: 'Opportunities', icon: PipelineIcon, shortLabel: 'Opps' },
{ href: '/implementation', label: 'Implementation', icon: ImplementIcon, shortLabel: 'Implement' },
{ href: '/goals', label: 'Goals & Targets', icon: GoalsIcon, shortLabel: 'Goals' },
{ href: '/playbooks', label: 'Playbooks', icon: PlaybookIcon, shortLabel: 'Plays' },
{ href: '/reports/status', label: 'Weekly Report', icon: ReportsIcon, shortLabel: 'Report' },
{ href: '/reports/executive', label: 'Exec Summary', icon: ExecIcon, shortLabel: 'Summary' },
];
function DashboardIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
@@ -80,6 +68,14 @@ function ExecIcon({ className }: { className?: string }) {
);
}
function AnalyticsIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" /><polyline points="7.5 4.21 12 6.81 16.5 4.21" /><polyline points="7.5 19.79 7.5 14.6 3 12" /><polyline points="21 12 16.5 14.6 16.5 19.79" /><polyline points="3.27 6.96 12 12.01 20.73 6.96" /><line x1="12" y1="22.08" x2="12" y2="12" />
</svg>
);
}
function AccountsIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
@@ -88,9 +84,31 @@ function AccountsIcon({ className }: { className?: string }) {
);
}
const navItems = [
{ href: '/', label: 'Executive Overview', icon: DashboardIcon, shortLabel: 'Overview' },
{ href: '/accounts', label: 'Accounts', icon: AccountsIcon, shortLabel: 'Accounts' },
{ href: '/activity', label: 'Activities', icon: ActivityIcon, shortLabel: 'Activities' },
{ href: '/pipeline', label: 'Opportunities', icon: PipelineIcon, shortLabel: 'Opps' },
{ href: '/implementation', label: 'Implementation', icon: ImplementIcon, shortLabel: 'Implement' },
{ href: '/goals', label: 'Goals & Targets', icon: GoalsIcon, shortLabel: 'Goals' },
{ href: '/playbooks', label: 'Playbooks', icon: PlaybookIcon, shortLabel: 'Plays' },
{ href: '/reports/status', label: 'Weekly Report', icon: ReportsIcon, shortLabel: 'Report' },
{ href: '/reports/executive', label: 'Exec Summary', icon: ExecIcon, shortLabel: 'Summary' },
];
const analyticsItems = [
{ href: '/analytics/pipeline-movement', label: 'Pipeline Movement' },
{ href: '/analytics/forecast', label: 'Forecast Workspace' },
{ href: '/analytics/forecast-accuracy', label: 'Forecast Accuracy' },
{ href: '/analytics/win-loss', label: 'Win/Loss Analysis' },
{ href: '/analytics/playbook-effectiveness', label: 'Playbook Effectiveness' },
];
export function Sidebar() {
const pathname = usePathname();
const [collapsed, setCollapsed] = useState(false);
const [analyticsOpen, setAnalyticsOpen] = useState(pathname.startsWith('/analytics'));
const isAnalyticsActive = pathname.startsWith('/analytics');
return (
<>
@@ -116,7 +134,7 @@ export function Sidebar() {
</svg>
</button>
</div>
<nav className="flex-1 py-4 space-y-1">
<nav className="flex-1 py-4 space-y-1 overflow-y-auto">
{navItems.map(item => {
const isActive = pathname === item.href;
return (
@@ -135,6 +153,50 @@ export function Sidebar() {
</Link>
);
})}
{/* Analytics section */}
{!collapsed ? (
<>
<button
onClick={() => setAnalyticsOpen(!analyticsOpen)}
className={`flex items-center gap-3 px-4 py-2.5 mx-2 rounded-lg transition-all w-full text-left ${
isAnalyticsActive ? 'bg-sidebar-active/20 text-white' : 'hover:bg-white/5 text-sidebar-text hover:text-white'
}`}
>
<AnalyticsIcon className={`w-5 h-5 flex-shrink-0 ${isAnalyticsActive ? 'text-sidebar-active' : ''}`} />
<span className="text-sm font-medium flex-1">Analytics</span>
<svg className={`w-3.5 h-3.5 transition-transform ${analyticsOpen ? 'rotate-90' : ''}`} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><polyline points="9 18 15 12 9 6" /></svg>
</button>
{analyticsOpen && (
<div className="ml-8 mr-2 space-y-0.5">
{analyticsItems.map(item => {
const isActive = pathname === item.href;
return (
<Link
key={item.href}
href={item.href}
className={`block px-3 py-1.5 rounded-md text-xs font-medium transition-all ${
isActive ? 'bg-sidebar-active/20 text-white' : 'text-sidebar-text/70 hover:text-white hover:bg-white/5'
}`}
>
{item.label}
</Link>
);
})}
</div>
)}
</>
) : (
<Link
href="/analytics/pipeline-movement"
className={`flex items-center justify-center px-2 py-2.5 mx-2 rounded-lg transition-all ${
isAnalyticsActive ? 'bg-sidebar-active/20 text-white' : 'hover:bg-white/5 text-sidebar-text hover:text-white'
}`}
title="Analytics"
>
<AnalyticsIcon className={`w-5 h-5 flex-shrink-0 ${isAnalyticsActive ? 'text-sidebar-active' : ''}`} />
</Link>
)}
</nav>
<div className={`${collapsed ? 'px-2' : 'px-4'} pb-4 space-y-3`}>
<Link