@decido/shell 1.0.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/package.json +17 -14
- package/.turbo/turbo-build.log +0 -13
- package/src/AgentPlayer.tsx +0 -105
- package/src/DecidoPlayer.tsx +0 -117
- package/src/bridge/BridgeAgent.ts +0 -443
- package/src/components/DecidoIcon.tsx +0 -56
- package/src/components/JsonTreeEditor.tsx +0 -117
- package/src/components/PanelSplitter.tsx +0 -71
- package/src/components/PluginErrorBoundary.tsx +0 -69
- package/src/components/SafeLiquidUI.tsx +0 -114
- package/src/components/TransientLayer.tsx +0 -92
- package/src/components/agent/AgentChat.tsx +0 -134
- package/src/components/chat-extensions/IntentCatalogPanel.tsx +0 -81
- package/src/components/chat-extensions/chatSlashCommands.ts +0 -101
- package/src/components/controls/CreatorInputBar.tsx +0 -144
- package/src/components/controls/OSToolbar.tsx +0 -90
- package/src/components/controls/TimelineTape.tsx +0 -43
- package/src/components/debug/ActionTimelineTab.tsx +0 -111
- package/src/components/debug/CSSInspectorTab.tsx +0 -436
- package/src/components/debug/ExportTab.tsx +0 -192
- package/src/components/debug/FlowHealthTab.tsx +0 -86
- package/src/components/debug/LogsTab.tsx +0 -110
- package/src/components/debug/MorphStackTab.tsx +0 -241
- package/src/components/debug/NetworkTab.tsx +0 -173
- package/src/components/debug/PerformanceTab.tsx +0 -171
- package/src/components/debug/ProfilesTab.tsx +0 -238
- package/src/components/debug/ReplayTab.tsx +0 -70
- package/src/components/debug/StoresTab.tsx +0 -255
- package/src/components/debug/TopologyTab.tsx +0 -59
- package/src/components/debug/debugConfig.tsx +0 -66
- package/src/components/playground/DebugPanel.tsx +0 -112
- package/src/components/playground/HeaderCenterControls.tsx +0 -92
- package/src/components/playground/KeyframeListItem.tsx +0 -70
- package/src/components/playground/PlaygroundAppSidebar.tsx +0 -171
- package/src/components/playground/PlaygroundBottomControls.tsx +0 -132
- package/src/components/playground/PlaygroundCanvas.tsx +0 -87
- package/src/components/playground/PlaygroundChat.tsx +0 -236
- package/src/components/playground/PlaygroundErrorBoundary.tsx +0 -63
- package/src/components/playground/PlaygroundFloatingInput.tsx +0 -352
- package/src/components/playground/PlaygroundHeader.tsx +0 -222
- package/src/components/playground/PlaygroundSidebar.tsx +0 -136
- package/src/components/playground/PlaygroundTerminal.tsx +0 -44
- package/src/components/playground/SuggestionCards.tsx +0 -29
- package/src/components/playground/demos/ClinicaAINode.tsx +0 -221
- package/src/components/playground/demos/FinanceAINode.tsx +0 -226
- package/src/components/playground/demos/KiaAcademyNode.tsx +0 -250
- package/src/components/playground/demos/KiaBotNode.tsx +0 -207
- package/src/components/playground/demos/KiaCampaignNode.tsx +0 -191
- package/src/components/playground/demos/KiaComplianceNode.tsx +0 -140
- package/src/components/playground/demos/KiaCustomerJourneyNode.tsx +0 -220
- package/src/components/playground/demos/KiaCyberNode.tsx +0 -203
- package/src/components/playground/demos/KiaDashboardNode.tsx +0 -399
- package/src/components/playground/demos/KiaEmbudoOverviewNode.tsx +0 -168
- package/src/components/playground/demos/KiaExecutiveNode.tsx +0 -169
- package/src/components/playground/demos/KiaGamificationNode.tsx +0 -229
- package/src/components/playground/demos/KiaIntelligenceHubNode.tsx +0 -165
- package/src/components/playground/demos/KiaInventoryNode.tsx +0 -183
- package/src/components/playground/demos/KiaLeadScoringNode.tsx +0 -226
- package/src/components/playground/demos/KiaLiveSimulationNode.tsx +0 -177
- package/src/components/playground/demos/KiaMultiDealerNode.tsx +0 -223
- package/src/components/playground/demos/KiaNPSVoiceNode.tsx +0 -214
- package/src/components/playground/demos/KiaOmnichannelNode.tsx +0 -162
- package/src/components/playground/demos/KiaPBIBudgetNode.tsx +0 -152
- package/src/components/playground/demos/KiaPBIConversionNode.tsx +0 -206
- package/src/components/playground/demos/KiaPBIFunnelNode.tsx +0 -184
- package/src/components/playground/demos/KiaPBIOwnershipNode.tsx +0 -113
- package/src/components/playground/demos/KiaPBIPartnerNode.tsx +0 -143
- package/src/components/playground/demos/KiaPBIPreciosNode.tsx +0 -120
- package/src/components/playground/demos/KiaPBIRuntNode.tsx +0 -205
- package/src/components/playground/demos/KiaPartnerScoreNode.tsx +0 -206
- package/src/components/playground/demos/KiaPredictiveNode.tsx +0 -226
- package/src/components/playground/demos/KiaShowroomNode.tsx +0 -194
- package/src/components/playground/demos/KiaStoreNode.tsx +0 -215
- package/src/components/playground/demos/KiaSustainabilityNode.tsx +0 -173
- package/src/components/playground/demos/KiaUsedVehiclesNode.tsx +0 -163
- package/src/components/playground/demos/KiaWorkshopNode.tsx +0 -221
- package/src/components/playground/demos/SmartCityNode.tsx +0 -205
- package/src/components/playground/demos/kia_campaign_manifest.json +0 -112
- package/src/components/playground/input-parts/AIModelSelector.tsx +0 -156
- package/src/components/playground/input-parts/InputActions.tsx +0 -80
- package/src/components/playground/input-parts/InputToolbar.tsx +0 -245
- package/src/components/playground/input-parts/ResourceLibraryPanel.tsx +0 -287
- package/src/components/playground/sidebarDsdIO.ts +0 -82
- package/src/components/settings/SettingsPanel.tsx +0 -267
- package/src/components/shell/AppHeader.tsx +0 -9
- package/src/components/shell/AppShell.tsx +0 -139
- package/src/components/shell/ArtifactBar.tsx +0 -97
- package/src/components/shell/BootScreen.tsx +0 -19
- package/src/components/shell/CenterComposite.tsx +0 -87
- package/src/components/shell/CodeEditorPanel.tsx +0 -88
- package/src/components/shell/GlobalOverlays.tsx +0 -228
- package/src/components/shell/LayoutConfigurator.tsx +0 -209
- package/src/components/shell/LayoutGrid.tsx +0 -178
- package/src/components/shell/MorphShell.tsx +0 -368
- package/src/components/shell/PluginViewer.tsx +0 -147
- package/src/components/shell/ShellNexusPreview.tsx +0 -458
- package/src/components/shell/SlotRenderer.tsx +0 -115
- package/src/components/shell/TabBar.tsx +0 -94
- package/src/components/shell/TemplateLibrary.tsx +0 -195
- package/src/components/shell/layoutConstants.ts +0 -35
- package/src/components/shell/morphStageMeta.ts +0 -15
- package/src/components/shell/shells/BuiltInShells.tsx +0 -443
- package/src/components/shell/shells/DatawayChatShell.tsx +0 -42
- package/src/components/shell/shells/TokenPreview.tsx +0 -339
- package/src/components/shell/shells/bootShells.ts +0 -31
- package/src/components/shells/CreatorShell.tsx +0 -37
- package/src/components/shells/DecidoShell.tsx +0 -447
- package/src/components/shells/ExperimentalChatShell.tsx +0 -245
- package/src/components/shells/UserCanvas.tsx +0 -44
- package/src/components/studio/BlueprintManagerPanel.tsx +0 -137
- package/src/components/studio/DependencyTreePanel.tsx +0 -192
- package/src/components/studio/NodePalette.tsx +0 -92
- package/src/components/studio/NodePropertiesPanel.tsx +0 -81
- package/src/components/studio/ReactFlowEditor.tsx +0 -242
- package/src/components/studio/TimelineEditor.tsx +0 -122
- package/src/components/studio/TimelineKeyframeCard.tsx +0 -99
- package/src/components/studio/VariablePanel.tsx +0 -181
- package/src/components/studio/blueprint/BlueprintCard.tsx +0 -82
- package/src/components/studio/editor/CanvasContextMenu.tsx +0 -107
- package/src/components/studio/editor/EditorToolbar.tsx +0 -80
- package/src/components/studio/editor/StageContentRenderer.tsx +0 -134
- package/src/components/studio/editor/TrackPropertyEditors.tsx +0 -133
- package/src/components/studio/editor/TreeNodeItem.tsx +0 -91
- package/src/components/studio/editor/edgeStyles.ts +0 -43
- package/src/components/studio/editor/editorKeyHandler.ts +0 -95
- package/src/components/studio/editor/nodeTypeRegistry.ts +0 -137
- package/src/components/studio/editor/paletteCatalog.tsx +0 -84
- package/src/components/studio/nodes/shell/InteractionNodes.tsx +0 -82
- package/src/components/studio/nodes/shell/LayoutControlNodes.tsx +0 -69
- package/src/components/studio/nodes/shell/RegisterActionNode.tsx +0 -20
- package/src/components/studio/nodes/shell/RegisterButtonNode.tsx +0 -22
- package/src/components/studio/nodes/shell/RegisterPanelNode.tsx +0 -19
- package/src/components/studio/nodes/shell/RegisterSidebarNode.tsx +0 -19
- package/src/components/studio/nodes/shell/RegisterStatusBarNode.tsx +0 -22
- package/src/components/studio/nodes/shell/RegisterTabNode.tsx +0 -21
- package/src/components/studio/nodes/shell/RegisterTopBarNode.tsx +0 -22
- package/src/components/studio/nodes/shell/ShellConfigNode.tsx +0 -51
- package/src/components/studio/nodes/shell/ShellNodeBase.tsx +0 -100
- package/src/components/studio/nodes/shell/ThemeNodes.tsx +0 -51
- package/src/components/studio/nodes/shell/index.ts +0 -12
- package/src/components/widgets/BroadcastWidget.tsx +0 -93
- package/src/components/widgets/MarketplaceWidget.tsx +0 -298
- package/src/components/widgets/McpToolsWidget.tsx +0 -231
- package/src/components/widgets/OpsDashboard.tsx +0 -59
- package/src/components/widgets/QuickActionsWidget.tsx +0 -60
- package/src/components/widgets/UsageWidget.tsx +0 -112
- package/src/components/widgets/WidgetRenderer.tsx +0 -892
- package/src/components/widgets/WidgetSlotPanel.tsx +0 -213
- package/src/config/IconRegistry.ts +0 -126
- package/src/contexts/NetworkProvider.tsx +0 -162
- package/src/core/AIDirector.ts +0 -71
- package/src/core/EventBus.ts +0 -37
- package/src/core/PluginContext.tsx +0 -141
- package/src/hooks/listeners/useUIStateListener.ts +0 -59
- package/src/hooks/listeners/useWhatsAppListener.ts +0 -110
- package/src/hooks/morphBridge.ts +0 -82
- package/src/hooks/useAIModelSelector.ts +0 -144
- package/src/hooks/useAgentStream.ts +0 -220
- package/src/hooks/useAutoUpdater.ts +0 -89
- package/src/hooks/useBootSequence.ts +0 -20
- package/src/hooks/useExportDSD.ts +0 -53
- package/src/hooks/useFullscreen.ts +0 -35
- package/src/hooks/useGeminiStream.ts +0 -282
- package/src/hooks/useIntentLens.ts +0 -224
- package/src/hooks/useKeyboardShortcuts.ts +0 -69
- package/src/hooks/useLoggerBridge.ts +0 -32
- package/src/hooks/useMcpClient.ts +0 -112
- package/src/hooks/useNexusaiDeploy.ts +0 -118
- package/src/hooks/usePlaybackEngine.ts +0 -21
- package/src/hooks/usePlaygroundCommander.ts +0 -475
- package/src/hooks/usePluginEngine.ts +0 -165
- package/src/hooks/useScreenRecorder.ts +0 -73
- package/src/hooks/useShellKeyboard.ts +0 -40
- package/src/hooks/useShellShortcuts.ts +0 -118
- package/src/hooks/useSoundEffects.ts +0 -35
- package/src/hooks/useStudioConfig.ts +0 -72
- package/src/hooks/useSystemBoot.ts +0 -84
- package/src/hooks/useSystemTelemetry.ts +0 -62
- package/src/lib/debugLogger.ts +0 -80
- package/src/lib/networkInterceptor.ts +0 -100
- package/src/mocks/decido.tsx +0 -41
- package/src/plugins/pluginAPI.ts +0 -190
- package/src/store/McpStore.ts +0 -69
- package/src/store/UpdaterStore.ts +0 -60
- package/src/store/engine.ts +0 -392
- package/src/store/index.ts +0 -4
- package/src/store/layoutPresets.ts +0 -66
- package/src/store/playgroundTypes.ts +0 -98
- package/src/store/useActionTimelineStore.ts +0 -48
- package/src/store/useDebugPanelStore.ts +0 -98
- package/src/store/useDebugProfileStore.ts +0 -130
- package/src/store/useLayoutStore.ts +0 -205
- package/src/store/useMorphInstanceStore.ts +0 -289
- package/src/store/useMorphologyStore.ts +0 -103
- package/src/store/usePlaygroundStore.ts +0 -236
- package/src/store/useShellRegistry.ts +0 -123
- package/src/store/useSuggestionsStore.ts +0 -57
- package/src/store/useThemeStore.ts +0 -399
- package/src/store/useUIComponentStore.ts +0 -179
- package/src/types/DecidoStoryDefinition.ts +0 -43
- package/src/utils/ai/ai-architect.ts +0 -92
- package/src/utils/ai/ai-code.ts +0 -187
- package/src/utils/ai/ai-core.ts +0 -50
- package/src/utils/ai/ai-media.ts +0 -292
- package/src/utils/layoutGraph.ts +0 -67
- package/tsconfig.json +0 -17
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { BaseNode } from '@decido/macia-core';
|
|
3
|
-
import {
|
|
4
|
-
Cpu, Activity, TrendingUp, AlertTriangle, CheckCircle,
|
|
5
|
-
Clock, Zap, BarChart3, Eye, Radio, Server, Gauge
|
|
6
|
-
} from 'lucide-react';
|
|
7
|
-
|
|
8
|
-
/* ═══════════════════════════════════════════════════════════════
|
|
9
|
-
KIA INTELLIGENCE HUB — Central AI Command · Grecco Motors
|
|
10
|
-
Unified view: todo el ecosistema en un solo nodo maestro
|
|
11
|
-
═══════════════════════════════════════════════════════════════ */
|
|
12
|
-
|
|
13
|
-
interface SystemNode {
|
|
14
|
-
id: string;
|
|
15
|
-
name: string;
|
|
16
|
-
status: 'online' | 'warning' | 'critical';
|
|
17
|
-
load: number;
|
|
18
|
-
events: number;
|
|
19
|
-
lastPing: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const SYSTEM_NODES: SystemNode[] = [
|
|
23
|
-
{ id: 'dash', name: 'Dashboard', status: 'online', load: 34, events: 1247, lastPing: '0.2s' },
|
|
24
|
-
{ id: 'embudo', name: 'Embudo', status: 'online', load: 28, events: 892, lastPing: '0.3s' },
|
|
25
|
-
{ id: 'bot', name: 'WhatsApp Bot', status: 'online', load: 67, events: 3421, lastPing: '0.1s' },
|
|
26
|
-
{ id: 'leads', name: 'Lead Scoring', status: 'online', load: 45, events: 567, lastPing: '0.4s' },
|
|
27
|
-
{ id: 'workshop', name: 'Workshop', status: 'online', load: 52, events: 1893, lastPing: '0.2s' },
|
|
28
|
-
{ id: 'fleet', name: 'Fleet Health', status: 'warning', load: 78, events: 234, lastPing: '1.2s' },
|
|
29
|
-
{ id: 'gamif', name: 'Gamification', status: 'online', load: 21, events: 456, lastPing: '0.3s' },
|
|
30
|
-
{ id: 'academy', name: 'Academy', status: 'online', load: 15, events: 189, lastPing: '0.5s' },
|
|
31
|
-
{ id: 'store', name: 'Store', status: 'online', load: 38, events: 712, lastPing: '0.2s' },
|
|
32
|
-
{ id: 'finance', name: 'Finance', status: 'online', load: 41, events: 923, lastPing: '0.3s' },
|
|
33
|
-
{ id: 'cyber', name: 'CyberOps', status: 'online', load: 89, events: 5672, lastPing: '0.1s' },
|
|
34
|
-
{ id: 'mobility', name: 'SmartMobility', status: 'online', load: 56, events: 1234, lastPing: '0.4s' },
|
|
35
|
-
{ id: 'sim', name: 'Live Simulation', status: 'online', load: 62, events: 2341, lastPing: '0.1s' },
|
|
36
|
-
{ id: 'partner', name: 'Partner Score', status: 'online', load: 12, events: 89, lastPing: '0.6s' },
|
|
37
|
-
{ id: 'journey', name: 'Customer Journey', status: 'online', load: 33, events: 567, lastPing: '0.3s' },
|
|
38
|
-
{ id: 'predictive', name: 'Predictive AI', status: 'online', load: 71, events: 1567, lastPing: '0.2s' },
|
|
39
|
-
{ id: 'multi', name: 'Multi-Dealer', status: 'online', load: 25, events: 345, lastPing: '0.4s' },
|
|
40
|
-
];
|
|
41
|
-
|
|
42
|
-
const AI_DECISIONS = [
|
|
43
|
-
{ time: '17:18', decision: 'Lead HOT María Gómez → Asignar Johan Blanco', confidence: 96, module: 'Lead Scoring' },
|
|
44
|
-
{ time: '17:15', decision: 'Reorder: Pastillas Freno Seltos × 50 unid', confidence: 91, module: 'Predictive AI' },
|
|
45
|
-
{ time: '17:12', decision: 'Churn Alert: Pedro Martínez → CX Manager', confidence: 82, module: 'Journey' },
|
|
46
|
-
{ time: '17:08', decision: 'V2G Grid Balance: +12kW al grid', confidence: 94, module: 'SmartMobility' },
|
|
47
|
-
{ time: '17:05', decision: 'Brute Force blocked → IP 45.33.X.X banned', confidence: 99, module: 'CyberOps' },
|
|
48
|
-
{ time: '17:01', decision: 'Lucky Drive winner → Mantenimiento gratis', confidence: 88, module: 'Gamification' },
|
|
49
|
-
{ time: '16:58', decision: 'Pre-aprobación: Score 812 Bancolombia $112M', confidence: 93, module: 'Finance' },
|
|
50
|
-
{ time: '16:55', decision: 'Cross-sell: Polarizado Nano → 94% prob', confidence: 94, module: 'Journey' },
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
export const KiaIntelligenceHubNode = (props: any) => {
|
|
54
|
-
const [nodes, setNodes] = useState(SYSTEM_NODES.map(n => ({ ...n })));
|
|
55
|
-
const [tab, setTab] = useState<'status' | 'decisions'>('status');
|
|
56
|
-
|
|
57
|
-
const onlineCount = nodes.filter(n => n.status === 'online').length;
|
|
58
|
-
const warningCount = nodes.filter(n => n.status === 'warning').length;
|
|
59
|
-
const totalEvents = nodes.reduce((s, n) => s + n.events, 0);
|
|
60
|
-
const avgLoad = Math.round(nodes.reduce((s, n) => s + n.load, 0) / nodes.length);
|
|
61
|
-
|
|
62
|
-
// Live jitter
|
|
63
|
-
useEffect(() => {
|
|
64
|
-
const interval = setInterval(() => {
|
|
65
|
-
setNodes(prev => prev.map(n => ({
|
|
66
|
-
...n,
|
|
67
|
-
load: Math.min(99, Math.max(5, n.load + Math.floor(Math.random() * 7 - 3))),
|
|
68
|
-
events: n.events + Math.floor(Math.random() * 5),
|
|
69
|
-
status: n.load > 90 ? 'critical' as const : n.load > 75 ? 'warning' as const : 'online' as const,
|
|
70
|
-
})));
|
|
71
|
-
}, 3000);
|
|
72
|
-
return () => clearInterval(interval);
|
|
73
|
-
}, []);
|
|
74
|
-
|
|
75
|
-
const statusColor = (s: string) =>
|
|
76
|
-
s === 'online' ? 'text-green-400' : s === 'warning' ? 'text-yellow-400' : 'text-red-400';
|
|
77
|
-
const statusBg = (s: string) =>
|
|
78
|
-
s === 'online' ? 'bg-green-500/20' : s === 'warning' ? 'bg-yellow-500/20' : 'bg-red-500/20';
|
|
79
|
-
|
|
80
|
-
return (
|
|
81
|
-
<BaseNode {...props} title="Kia Intelligence Hub" width={520} height={640}>
|
|
82
|
-
<div className="flex flex-col h-full bg-[#050810] text-white overflow-hidden">
|
|
83
|
-
|
|
84
|
-
{/* Header */}
|
|
85
|
-
<div className="flex items-center gap-3 p-3 bg-[#0a1020] border-b border-slate-800">
|
|
86
|
-
<div className="w-10 h-10 rounded-full bg-linear-to-br from-red-600/30 to-purple-600/30 flex items-center justify-center border border-red-500/20">
|
|
87
|
-
<Cpu size={18} className="text-red-400" />
|
|
88
|
-
</div>
|
|
89
|
-
<div className="flex-1">
|
|
90
|
-
<div className="text-xs font-bold tracking-wide flex items-center gap-2">
|
|
91
|
-
INTELLIGENCE HUB
|
|
92
|
-
<span className="w-1.5 h-1.5 rounded-full bg-green-400 animate-pulse" />
|
|
93
|
-
</div>
|
|
94
|
-
<div className="text-[10px] text-slate-400">Grecco Motors · AI Command Center · {nodes.length} sistemas</div>
|
|
95
|
-
</div>
|
|
96
|
-
<div className="flex gap-1 bg-slate-900 rounded-lg p-0.5 border border-slate-800">
|
|
97
|
-
<button onClick={() => setTab('status')}
|
|
98
|
-
className={`px-2 py-1 rounded text-[9px] font-bold ${tab === 'status' ? 'bg-red-600 text-white' : 'text-slate-500'}`}>
|
|
99
|
-
🖥️ Estado
|
|
100
|
-
</button>
|
|
101
|
-
<button onClick={() => setTab('decisions')}
|
|
102
|
-
className={`px-2 py-1 rounded text-[9px] font-bold ${tab === 'decisions' ? 'bg-red-600 text-white' : 'text-slate-500'}`}>
|
|
103
|
-
🧠 IA Decisions
|
|
104
|
-
</button>
|
|
105
|
-
</div>
|
|
106
|
-
</div>
|
|
107
|
-
|
|
108
|
-
{/* KPIs */}
|
|
109
|
-
<div className="grid grid-cols-4 gap-1 p-2 bg-[#080e1a] border-b border-slate-800">
|
|
110
|
-
<div className="text-center bg-green-500/5 border border-green-500/10 rounded p-1.5">
|
|
111
|
-
<div className="text-base font-bold text-green-400">{onlineCount}</div>
|
|
112
|
-
<div className="text-[7px] text-slate-500">ONLINE</div>
|
|
113
|
-
</div>
|
|
114
|
-
<div className="text-center bg-yellow-500/5 border border-yellow-500/10 rounded p-1.5">
|
|
115
|
-
<div className="text-base font-bold text-yellow-400">{warningCount}</div>
|
|
116
|
-
<div className="text-[7px] text-slate-500">WARNING</div>
|
|
117
|
-
</div>
|
|
118
|
-
<div className="text-center bg-blue-500/5 border border-blue-500/10 rounded p-1.5">
|
|
119
|
-
<div className="text-base font-bold text-blue-400">{(totalEvents / 1000).toFixed(1)}k</div>
|
|
120
|
-
<div className="text-[7px] text-slate-500">EVENTS</div>
|
|
121
|
-
</div>
|
|
122
|
-
<div className="text-center bg-purple-500/5 border border-purple-500/10 rounded p-1.5">
|
|
123
|
-
<div className="text-base font-bold text-purple-400">{avgLoad}%</div>
|
|
124
|
-
<div className="text-[7px] text-slate-500">AVG LOAD</div>
|
|
125
|
-
</div>
|
|
126
|
-
</div>
|
|
127
|
-
|
|
128
|
-
{/* Content */}
|
|
129
|
-
<div className="flex-1 overflow-y-auto p-2 space-y-1">
|
|
130
|
-
|
|
131
|
-
{tab === 'status' && nodes.map(n => (
|
|
132
|
-
<div key={n.id} className="flex items-center gap-2 bg-slate-900/40 rounded-lg px-2.5 py-1.5 border border-slate-800/50">
|
|
133
|
-
<div className={`w-1.5 h-1.5 rounded-full ${n.status === 'online' ? 'bg-green-400' : n.status === 'warning' ? 'bg-yellow-400 animate-pulse' : 'bg-red-400 animate-pulse'}`} />
|
|
134
|
-
<span className="text-[10px] font-semibold w-28 truncate">{n.name}</span>
|
|
135
|
-
<div className="flex-1 h-1 bg-slate-800 rounded-full">
|
|
136
|
-
<div className={`h-full rounded-full transition-all duration-500 ${n.load > 80 ? 'bg-red-500' : n.load > 60 ? 'bg-yellow-500' : 'bg-green-500'
|
|
137
|
-
}`} style={{ width: `${n.load}%` }} />
|
|
138
|
-
</div>
|
|
139
|
-
<span className={`text-[9px] font-mono w-8 text-right ${n.load > 80 ? 'text-red-400' : n.load > 60 ? 'text-yellow-400' : 'text-green-400'
|
|
140
|
-
}`}>{n.load}%</span>
|
|
141
|
-
<span className="text-[8px] text-slate-600 w-8 text-right">{n.lastPing}</span>
|
|
142
|
-
</div>
|
|
143
|
-
))}
|
|
144
|
-
|
|
145
|
-
{tab === 'decisions' && AI_DECISIONS.map((d, i) => (
|
|
146
|
-
<div key={i} className="bg-slate-900/40 rounded-lg p-2.5 border border-slate-800/50"
|
|
147
|
-
style={{ borderLeftWidth: '2px', borderLeftColor: d.confidence > 90 ? '#22c55e' : d.confidence > 80 ? '#3b82f6' : '#f59e0b' }}>
|
|
148
|
-
<div className="flex items-center justify-between mb-0.5">
|
|
149
|
-
<span className="text-[9px] font-mono text-slate-500">{d.time}</span>
|
|
150
|
-
<span className="text-[8px] px-1.5 py-0.5 bg-purple-500/20 text-purple-400 rounded">{d.module}</span>
|
|
151
|
-
</div>
|
|
152
|
-
<div className="text-[10px] text-slate-300">{d.decision}</div>
|
|
153
|
-
<div className="flex items-center gap-1 mt-1">
|
|
154
|
-
<div className="flex-1 h-0.5 bg-slate-800 rounded-full">
|
|
155
|
-
<div className="h-full rounded-full bg-green-500" style={{ width: `${d.confidence}%` }} />
|
|
156
|
-
</div>
|
|
157
|
-
<span className="text-[8px] text-green-400 font-bold">{d.confidence}%</span>
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
))}
|
|
161
|
-
</div>
|
|
162
|
-
</div>
|
|
163
|
-
</BaseNode>
|
|
164
|
-
);
|
|
165
|
-
};
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { BaseNode } from '@decido/macia-core';
|
|
3
|
-
import {
|
|
4
|
-
Package, Truck, AlertTriangle, Clock, TrendingUp,
|
|
5
|
-
BarChart3, RefreshCw, MapPin, ChevronUp, ChevronDown, ShoppingCart
|
|
6
|
-
} from 'lucide-react';
|
|
7
|
-
|
|
8
|
-
/* ═══════════════════════════════════════════════════════════════
|
|
9
|
-
KIA INVENTORY NODE — Inventario & Supply Chain · Grecco Motors
|
|
10
|
-
Stock vehículos, aging, rotación, supply chain, auto-reorder
|
|
11
|
-
═══════════════════════════════════════════════════════════════ */
|
|
12
|
-
|
|
13
|
-
interface Vehicle {
|
|
14
|
-
model: string;
|
|
15
|
-
color: string;
|
|
16
|
-
stock: number;
|
|
17
|
-
reserved: number;
|
|
18
|
-
transit: number;
|
|
19
|
-
aging: number;
|
|
20
|
-
rotation: number;
|
|
21
|
-
status: 'ok' | 'low' | 'critical';
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const VEHICLES: Vehicle[] = [
|
|
25
|
-
{ model: 'Picanto Vibrant', color: 'Gray Pearl', stock: 8, reserved: 3, transit: 4, aging: 18, rotation: 5.8, status: 'ok' },
|
|
26
|
-
{ model: 'K3 Cross Zenith', color: 'Snow White', stock: 5, reserved: 2, transit: 3, aging: 15, rotation: 4.2, status: 'ok' },
|
|
27
|
-
{ model: 'Sportage HEV', color: 'Steel Gray', stock: 12, reserved: 4, transit: 6, aging: 22, rotation: 3.1, status: 'ok' },
|
|
28
|
-
{ model: 'Stonic Master', color: 'Orange', stock: 15, reserved: 1, transit: 0, aging: 45, rotation: 2.1, status: 'low' },
|
|
29
|
-
{ model: 'Sonet Desire', color: 'Blue', stock: 9, reserved: 2, transit: 3, aging: 25, rotation: 3.5, status: 'ok' },
|
|
30
|
-
{ model: 'Tasman 4x4', color: 'Black', stock: 6, reserved: 3, transit: 4, aging: 8, rotation: 6.2, status: 'ok' },
|
|
31
|
-
{ model: 'EV5 Earth', color: 'White Pearl', stock: 4, reserved: 2, transit: 2, aging: 12, rotation: 4.0, status: 'low' },
|
|
32
|
-
];
|
|
33
|
-
const SHIPMENTS = [
|
|
34
|
-
{ id: 'KIA-2026-0284', origin: 'Hwaseong, Korea', models: 'EV6 ×4, EV9 ×2', eta: 'Mar 8', status: 'En tránsito', progress: 72 },
|
|
35
|
-
{ id: 'KIA-2026-0291', origin: 'Gwangju, Korea', models: 'Sportage ×5, Sorento ×3', eta: 'Mar 15', status: 'Embarcado', progress: 45 },
|
|
36
|
-
{ id: 'KIA-2026-0303', origin: 'Saltillo, México', models: 'K3 ×8, Seltos ×6', eta: 'Feb 28', status: 'Aduana COL', progress: 88 },
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
const PARTS_ALERTS = [
|
|
40
|
-
{ part: 'Pastillas Freno Seltos', stock: 12, min: 20, reorderQty: 50, supplier: 'Hyundai Mobis', eta: '5 días' },
|
|
41
|
-
{ part: 'Batería Aux EV6', stock: 4, min: 8, reorderQty: 20, supplier: 'LG Energy', eta: '12 días' },
|
|
42
|
-
{ part: 'Filtro Cabina Sportage', stock: 6, min: 15, reorderQty: 40, supplier: 'Mann Filter', eta: '3 días' },
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
export const KiaInventoryNode = (props: any) => {
|
|
46
|
-
const [tab, setTab] = useState<'stock' | 'supply' | 'parts'>('stock');
|
|
47
|
-
const [vehicles, setVehicles] = useState(VEHICLES.map(v => ({ ...v })));
|
|
48
|
-
|
|
49
|
-
const totalStock = vehicles.reduce((s, v) => s + v.stock, 0);
|
|
50
|
-
const totalReserved = vehicles.reduce((s, v) => s + v.reserved, 0);
|
|
51
|
-
const totalTransit = vehicles.reduce((s, v) => s + v.transit, 0);
|
|
52
|
-
const avgAging = Math.round(vehicles.filter(v => v.aging > 0).reduce((s, v) => s + v.aging, 0) / vehicles.filter(v => v.aging > 0).length);
|
|
53
|
-
|
|
54
|
-
// Jitter
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
const interval = setInterval(() => {
|
|
57
|
-
setVehicles(prev => prev.map(v => ({
|
|
58
|
-
...v,
|
|
59
|
-
aging: v.stock > 0 ? v.aging + 1 : 0,
|
|
60
|
-
stock: Math.max(0, v.stock + (Math.random() > 0.8 ? (Math.random() > 0.5 ? 1 : -1) : 0)),
|
|
61
|
-
})));
|
|
62
|
-
}, 10000);
|
|
63
|
-
return () => clearInterval(interval);
|
|
64
|
-
}, []);
|
|
65
|
-
|
|
66
|
-
return (
|
|
67
|
-
<BaseNode {...props} title="Kia Inventory" width={500} height={620}>
|
|
68
|
-
<div className="flex flex-col h-full bg-[#060a12] text-white overflow-hidden">
|
|
69
|
-
|
|
70
|
-
{/* Header */}
|
|
71
|
-
<div className="flex items-center gap-3 p-3 bg-[#0c1222] border-b border-slate-800">
|
|
72
|
-
<div className="w-9 h-9 rounded-full bg-orange-600/20 flex items-center justify-center border border-orange-500/30">
|
|
73
|
-
<Package size={16} className="text-orange-400" />
|
|
74
|
-
</div>
|
|
75
|
-
<div className="flex-1">
|
|
76
|
-
<div className="text-xs font-bold tracking-wide">INVENTARIO & SUPPLY CHAIN</div>
|
|
77
|
-
<div className="text-[10px] text-slate-400">Grecco Motors · {totalStock} en stock · {totalTransit} en tránsito</div>
|
|
78
|
-
</div>
|
|
79
|
-
</div>
|
|
80
|
-
|
|
81
|
-
{/* Tabs */}
|
|
82
|
-
<div className="flex gap-1 p-2 bg-[#0a0f1a] border-b border-slate-800">
|
|
83
|
-
{([
|
|
84
|
-
{ key: 'stock' as const, label: '🚗 Stock' },
|
|
85
|
-
{ key: 'supply' as const, label: '🚢 Supply Chain' },
|
|
86
|
-
{ key: 'parts' as const, label: '⚠️ Repuestos' },
|
|
87
|
-
]).map(t => (
|
|
88
|
-
<button key={t.key} onClick={() => setTab(t.key)}
|
|
89
|
-
className={`flex-1 py-1.5 rounded text-[9px] font-bold transition-all ${tab === t.key ? 'bg-orange-600/20 text-orange-400 border border-orange-500/30' : 'text-slate-500'
|
|
90
|
-
}`}>
|
|
91
|
-
{t.label}
|
|
92
|
-
</button>
|
|
93
|
-
))}
|
|
94
|
-
</div>
|
|
95
|
-
|
|
96
|
-
{/* KPIs */}
|
|
97
|
-
<div className="flex gap-1 px-3 py-2 border-b border-slate-800">
|
|
98
|
-
{[
|
|
99
|
-
{ label: 'Stock', val: totalStock.toString(), color: 'text-blue-400' },
|
|
100
|
-
{ label: 'Reservados', val: totalReserved.toString(), color: 'text-purple-400' },
|
|
101
|
-
{ label: 'En Tránsito', val: totalTransit.toString(), color: 'text-green-400' },
|
|
102
|
-
{ label: 'Aging Prom', val: `${avgAging}d`, color: 'text-yellow-400' },
|
|
103
|
-
].map(s => (
|
|
104
|
-
<div key={s.label} className="flex-1 text-center bg-slate-900/50 rounded p-1.5 border border-slate-800/50">
|
|
105
|
-
<div className={`text-sm font-bold ${s.color}`}>{s.val}</div>
|
|
106
|
-
<div className="text-[8px] text-slate-500">{s.label}</div>
|
|
107
|
-
</div>
|
|
108
|
-
))}
|
|
109
|
-
</div>
|
|
110
|
-
|
|
111
|
-
{/* Content */}
|
|
112
|
-
<div className="flex-1 overflow-y-auto p-3 space-y-1.5">
|
|
113
|
-
|
|
114
|
-
{tab === 'stock' && vehicles.map(v => (
|
|
115
|
-
<div key={v.model} className="bg-slate-900/50 rounded-lg p-2.5 border border-slate-800"
|
|
116
|
-
style={{ borderLeftWidth: '2px', borderLeftColor: v.status === 'ok' ? '#22c55e' : v.status === 'low' ? '#f59e0b' : '#ef4444' }}>
|
|
117
|
-
<div className="flex items-center justify-between mb-1">
|
|
118
|
-
<div>
|
|
119
|
-
<span className="text-[11px] font-bold">{v.model}</span>
|
|
120
|
-
<span className="text-[9px] text-slate-500 ml-1.5">{v.color}</span>
|
|
121
|
-
</div>
|
|
122
|
-
<span className={`text-[8px] px-1.5 py-0.5 rounded font-bold ${v.status === 'ok' ? 'bg-green-500/20 text-green-400' :
|
|
123
|
-
v.status === 'low' ? 'bg-yellow-500/20 text-yellow-400' :
|
|
124
|
-
'bg-red-500/20 text-red-400 animate-pulse'
|
|
125
|
-
}`}>{v.status === 'ok' ? 'OK' : v.status === 'low' ? 'BAJO' : 'CRÍTICO'}</span>
|
|
126
|
-
</div>
|
|
127
|
-
<div className="flex gap-3 text-[9px]">
|
|
128
|
-
<span className="text-slate-400">Stock: <span className="font-bold text-white">{v.stock}</span></span>
|
|
129
|
-
<span className="text-slate-400">Reserv: <span className="font-bold text-purple-400">{v.reserved}</span></span>
|
|
130
|
-
<span className="text-slate-400">Transit: <span className="font-bold text-green-400">{v.transit}</span></span>
|
|
131
|
-
<span className="text-slate-400">Aging: <span className={`font-bold ${v.aging > 30 ? 'text-red-400' : 'text-slate-300'}`}>{v.aging}d</span></span>
|
|
132
|
-
<span className="text-slate-400">Rot: <span className="font-bold text-blue-400">{v.rotation}×</span></span>
|
|
133
|
-
</div>
|
|
134
|
-
</div>
|
|
135
|
-
))}
|
|
136
|
-
|
|
137
|
-
{tab === 'supply' && SHIPMENTS.map(s => (
|
|
138
|
-
<div key={s.id} className="bg-slate-900/50 rounded-lg p-3 border border-slate-800">
|
|
139
|
-
<div className="flex items-center justify-between mb-1">
|
|
140
|
-
<span className="text-[10px] font-bold text-white">{s.id}</span>
|
|
141
|
-
<span className="text-[9px] text-green-400 font-semibold">{s.status}</span>
|
|
142
|
-
</div>
|
|
143
|
-
<div className="text-[9px] text-slate-400 mb-1">
|
|
144
|
-
<MapPin size={9} className="inline mr-1" />{s.origin} → Medellín
|
|
145
|
-
</div>
|
|
146
|
-
<div className="text-[9px] text-slate-500 mb-2">{s.models}</div>
|
|
147
|
-
<div className="flex items-center gap-2">
|
|
148
|
-
<div className="flex-1 h-1.5 bg-slate-800 rounded-full">
|
|
149
|
-
<div className="h-full rounded-full bg-linear-to-r from-blue-500 to-green-500 transition-all"
|
|
150
|
-
style={{ width: `${s.progress}%` }} />
|
|
151
|
-
</div>
|
|
152
|
-
<span className="text-[9px] font-bold text-blue-400">{s.progress}%</span>
|
|
153
|
-
<span className="text-[8px] text-slate-500">ETA: {s.eta}</span>
|
|
154
|
-
</div>
|
|
155
|
-
</div>
|
|
156
|
-
))}
|
|
157
|
-
|
|
158
|
-
{tab === 'parts' && (
|
|
159
|
-
<>
|
|
160
|
-
<div className="text-[9px] text-red-400 font-bold uppercase mb-1 flex items-center gap-1">
|
|
161
|
-
<AlertTriangle size={10} /> Alertas de Reabastecimiento
|
|
162
|
-
</div>
|
|
163
|
-
{PARTS_ALERTS.map(p => (
|
|
164
|
-
<div key={p.part} className="bg-red-500/5 rounded-lg p-2.5 border border-red-500/20">
|
|
165
|
-
<div className="flex items-center justify-between mb-1">
|
|
166
|
-
<span className="text-[10px] font-bold">{p.part}</span>
|
|
167
|
-
<span className="text-[8px] px-1.5 py-0.5 bg-red-500/20 text-red-400 rounded font-bold animate-pulse">REORDER</span>
|
|
168
|
-
</div>
|
|
169
|
-
<div className="flex gap-3 text-[9px] mb-1">
|
|
170
|
-
<span className="text-red-400">Stock: {p.stock}</span>
|
|
171
|
-
<span className="text-slate-500">Min: {p.min}</span>
|
|
172
|
-
<span className="text-green-400">Pedir: {p.reorderQty}</span>
|
|
173
|
-
</div>
|
|
174
|
-
<div className="text-[8px] text-slate-500">{p.supplier} · ETA: {p.eta}</div>
|
|
175
|
-
</div>
|
|
176
|
-
))}
|
|
177
|
-
</>
|
|
178
|
-
)}
|
|
179
|
-
</div>
|
|
180
|
-
</div>
|
|
181
|
-
</BaseNode>
|
|
182
|
-
);
|
|
183
|
-
};
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { BaseNode } from '@decido/macia-core';
|
|
3
|
-
import {
|
|
4
|
-
Activity,
|
|
5
|
-
Users,
|
|
6
|
-
Flame,
|
|
7
|
-
ThermometerSun,
|
|
8
|
-
Snowflake,
|
|
9
|
-
TrendingUp,
|
|
10
|
-
Phone,
|
|
11
|
-
MessageSquare,
|
|
12
|
-
Mail,
|
|
13
|
-
Clock,
|
|
14
|
-
Zap,
|
|
15
|
-
Filter,
|
|
16
|
-
ArrowRight,
|
|
17
|
-
} from 'lucide-react';
|
|
18
|
-
|
|
19
|
-
/* ═══════════════════════════════════════════════════════════════
|
|
20
|
-
KIA LEAD SCORING NODE
|
|
21
|
-
Visual pipeline de clasificación de leads con scoring IA
|
|
22
|
-
═══════════════════════════════════════════════════════════════ */
|
|
23
|
-
|
|
24
|
-
interface Lead {
|
|
25
|
-
id: number;
|
|
26
|
-
name: string;
|
|
27
|
-
vehicle: string;
|
|
28
|
-
channel: string;
|
|
29
|
-
score: number;
|
|
30
|
-
status: 'hot' | 'warm' | 'cold' | 'nurture';
|
|
31
|
-
lastAction: string;
|
|
32
|
-
time: string;
|
|
33
|
-
assignedTo?: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const INITIAL_LEADS: Lead[] = [
|
|
37
|
-
{ id: 1, name: 'Juan Pérez', vehicle: 'EV6 GT', channel: 'Facebook', score: 95, status: 'hot', lastAction: 'Agendó Test Drive', time: '2m', assignedTo: 'Johan Blanco' },
|
|
38
|
-
{ id: 2, name: 'Andrea López', vehicle: 'Sportage', channel: 'Google Ads', score: 88, status: 'hot', lastAction: 'Preguntó financiación', time: '5m' },
|
|
39
|
-
{ id: 3, name: 'Carlos Ruiz', vehicle: 'K3 Cross', channel: 'Web', score: 72, status: 'warm', lastAction: 'Comparó modelos', time: '12m' },
|
|
40
|
-
{ id: 4, name: 'María Mendez', vehicle: 'Picanto', channel: 'Instagram', score: 45, status: 'cold', lastAction: 'Solo dio like', time: '1h' },
|
|
41
|
-
{ id: 5, name: 'Pedro Gómez', vehicle: 'Seltos', channel: 'Walk-in', score: 68, status: 'warm', lastAction: 'Visitó showroom', time: '20m' },
|
|
42
|
-
{ id: 6, name: 'Luisa Perea', vehicle: 'Sorento', channel: 'WhatsApp', score: 82, status: 'hot', lastAction: 'Pidió cotización', time: '8m' },
|
|
43
|
-
{ id: 7, name: 'Diego Torres', vehicle: 'Stonic', channel: 'BTL', score: 35, status: 'nurture', lastAction: 'Registró datos ruleta', time: '2h' },
|
|
44
|
-
{ id: 8, name: 'Camila Rey', vehicle: 'EV3', channel: 'Google Ads', score: 91, status: 'hot', lastAction: 'Pre-aprobación crédito', time: '3m', assignedTo: 'Camila Torres' },
|
|
45
|
-
];
|
|
46
|
-
|
|
47
|
-
const STATUS_CONFIG = {
|
|
48
|
-
hot: { icon: Flame, label: 'Caliente', color: '#ef4444', bg: '#ef444422' },
|
|
49
|
-
warm: { icon: ThermometerSun, label: 'Tibio', color: '#f59e0b', bg: '#f59e0b22' },
|
|
50
|
-
cold: { icon: Snowflake, label: 'Frío', color: '#3b82f6', bg: '#3b82f622' },
|
|
51
|
-
nurture: { icon: Activity, label: 'Nurture', color: '#a855f7', bg: '#a855f722' },
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const CHANNEL_ICON: Record<string, typeof Phone> = {
|
|
55
|
-
Facebook: MessageSquare,
|
|
56
|
-
Instagram: MessageSquare,
|
|
57
|
-
'Google Ads': TrendingUp,
|
|
58
|
-
Web: TrendingUp,
|
|
59
|
-
WhatsApp: Phone,
|
|
60
|
-
'Walk-in': Users,
|
|
61
|
-
BTL: Users,
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const jitter = (n: number, r: number) => n + Math.round((Math.random() - 0.5) * r);
|
|
65
|
-
|
|
66
|
-
export const KiaLeadScoringNode = (props: any) => {
|
|
67
|
-
const [leads, setLeads] = useState(INITIAL_LEADS);
|
|
68
|
-
const [filter, setFilter] = useState<'all' | 'hot' | 'warm' | 'cold' | 'nurture'>('all');
|
|
69
|
-
const [tick, setTick] = useState(0);
|
|
70
|
-
|
|
71
|
-
/* Simulate new leads arriving */
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
const iv = setInterval(() => {
|
|
74
|
-
setTick(t => t + 1);
|
|
75
|
-
setLeads(prev => prev.map(l => ({
|
|
76
|
-
...l,
|
|
77
|
-
score: Math.min(100, Math.max(10, jitter(l.score, 4))),
|
|
78
|
-
})));
|
|
79
|
-
}, 6000);
|
|
80
|
-
return () => clearInterval(iv);
|
|
81
|
-
}, []);
|
|
82
|
-
|
|
83
|
-
const filtered = filter === 'all' ? leads : leads.filter(l => l.status === filter);
|
|
84
|
-
const counts = {
|
|
85
|
-
hot: leads.filter(l => l.status === 'hot').length,
|
|
86
|
-
warm: leads.filter(l => l.status === 'warm').length,
|
|
87
|
-
cold: leads.filter(l => l.status === 'cold').length,
|
|
88
|
-
nurture: leads.filter(l => l.status === 'nurture').length,
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<BaseNode {...props} title="Kia Lead Scoring — IA" width={520} height={620}>
|
|
93
|
-
<div className="flex flex-col h-full bg-slate-900 text-white p-4 gap-3 overflow-hidden">
|
|
94
|
-
|
|
95
|
-
{/* Header */}
|
|
96
|
-
<div className="flex justify-between items-center">
|
|
97
|
-
<div>
|
|
98
|
-
<h2 className="text-sm font-bold tracking-wide">SCORING DE LEADS</h2>
|
|
99
|
-
<div className="flex items-center gap-2 mt-0.5">
|
|
100
|
-
<span className="text-[10px] text-slate-400">Clasificación IA · Tiempo Real</span>
|
|
101
|
-
<span className="flex items-center gap-1 text-[10px] text-green-400 bg-green-500/10 px-1.5 rounded">
|
|
102
|
-
<Activity size={8} className="animate-pulse" /> LIVE
|
|
103
|
-
</span>
|
|
104
|
-
</div>
|
|
105
|
-
</div>
|
|
106
|
-
<div className="text-right">
|
|
107
|
-
<div className="text-lg font-bold text-white">{leads.length}</div>
|
|
108
|
-
<div className="text-[9px] text-slate-500">Leads Activos</div>
|
|
109
|
-
</div>
|
|
110
|
-
</div>
|
|
111
|
-
|
|
112
|
-
{/* Pipeline Summary */}
|
|
113
|
-
<div className="grid grid-cols-4 gap-2">
|
|
114
|
-
{(['hot', 'warm', 'cold', 'nurture'] as const).map(status => {
|
|
115
|
-
const cfg = STATUS_CONFIG[status];
|
|
116
|
-
const Icon = cfg.icon;
|
|
117
|
-
const active = filter === status;
|
|
118
|
-
return (
|
|
119
|
-
<button
|
|
120
|
-
key={status}
|
|
121
|
-
onClick={() => setFilter(f => f === status ? 'all' : status)}
|
|
122
|
-
className={`p-2 rounded-lg border transition-all text-center cursor-pointer ${active
|
|
123
|
-
? 'border-white/30 scale-105'
|
|
124
|
-
: 'border-slate-700 hover:border-slate-600'
|
|
125
|
-
}`}
|
|
126
|
-
style={{ background: active ? cfg.bg : '#1e293b' }}
|
|
127
|
-
>
|
|
128
|
-
<Icon size={14} className="mx-auto mb-1" style={{ color: cfg.color }} />
|
|
129
|
-
<div className="text-lg font-bold" style={{ color: cfg.color }}>{counts[status]}</div>
|
|
130
|
-
<div className="text-[9px] text-slate-500">{cfg.label}</div>
|
|
131
|
-
</button>
|
|
132
|
-
);
|
|
133
|
-
})}
|
|
134
|
-
</div>
|
|
135
|
-
|
|
136
|
-
{/* Conversion Funnel Mini */}
|
|
137
|
-
<div className="flex items-center gap-1 bg-slate-800 rounded-lg p-2 border border-slate-700">
|
|
138
|
-
<div className="flex items-center gap-1 flex-1 text-center">
|
|
139
|
-
<div className="flex-1">
|
|
140
|
-
<div className="text-xs font-bold text-purple-400">{leads.length}</div>
|
|
141
|
-
<div className="text-[8px] text-slate-500">Total</div>
|
|
142
|
-
</div>
|
|
143
|
-
<ArrowRight size={10} className="text-slate-600" />
|
|
144
|
-
<div className="flex-1">
|
|
145
|
-
<div className="text-xs font-bold text-yellow-400">{counts.hot + counts.warm}</div>
|
|
146
|
-
<div className="text-[8px] text-slate-500">Calificados</div>
|
|
147
|
-
</div>
|
|
148
|
-
<ArrowRight size={10} className="text-slate-600" />
|
|
149
|
-
<div className="flex-1">
|
|
150
|
-
<div className="text-xs font-bold text-green-400">{counts.hot}</div>
|
|
151
|
-
<div className="text-[8px] text-slate-500">Calientes</div>
|
|
152
|
-
</div>
|
|
153
|
-
<ArrowRight size={10} className="text-slate-600" />
|
|
154
|
-
<div className="flex-1">
|
|
155
|
-
<div className="text-xs font-bold text-red-400">{leads.filter(l => l.assignedTo).length}</div>
|
|
156
|
-
<div className="text-[8px] text-slate-500">Asignados</div>
|
|
157
|
-
</div>
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
|
|
161
|
-
{/* Lead List */}
|
|
162
|
-
<div className="flex-1 overflow-y-auto space-y-1.5 pr-1" key={tick}>
|
|
163
|
-
{filtered.sort((a, b) => b.score - a.score).map(lead => {
|
|
164
|
-
const cfg = STATUS_CONFIG[lead.status];
|
|
165
|
-
const ChannelIcon = CHANNEL_ICON[lead.channel] || Users;
|
|
166
|
-
return (
|
|
167
|
-
<div
|
|
168
|
-
key={lead.id}
|
|
169
|
-
className="bg-slate-800 rounded-lg p-2.5 border border-slate-700 hover:border-slate-500 transition-all cursor-pointer group"
|
|
170
|
-
>
|
|
171
|
-
<div className="flex items-center justify-between">
|
|
172
|
-
<div className="flex items-center gap-2 flex-1 min-w-0">
|
|
173
|
-
{/* Score Circle */}
|
|
174
|
-
<div
|
|
175
|
-
className="w-9 h-9 rounded-full flex items-center justify-center font-bold text-xs shrink-0 border-2"
|
|
176
|
-
style={{ borderColor: cfg.color, color: cfg.color, background: cfg.bg }}
|
|
177
|
-
>
|
|
178
|
-
{lead.score}
|
|
179
|
-
</div>
|
|
180
|
-
<div className="min-w-0 flex-1">
|
|
181
|
-
<div className="flex items-center gap-1.5">
|
|
182
|
-
<span className="text-xs font-medium text-white truncate">{lead.name}</span>
|
|
183
|
-
<cfg.icon size={10} style={{ color: cfg.color }} />
|
|
184
|
-
</div>
|
|
185
|
-
<div className="flex items-center gap-1.5 text-[9px] text-slate-500">
|
|
186
|
-
<span>{lead.vehicle}</span>
|
|
187
|
-
<span>·</span>
|
|
188
|
-
<ChannelIcon size={8} />
|
|
189
|
-
<span>{lead.channel}</span>
|
|
190
|
-
<span>·</span>
|
|
191
|
-
<Clock size={8} />
|
|
192
|
-
<span>{lead.time}</span>
|
|
193
|
-
</div>
|
|
194
|
-
</div>
|
|
195
|
-
</div>
|
|
196
|
-
<div className="text-right shrink-0 ml-2">
|
|
197
|
-
<div className="text-[9px] text-slate-400 truncate max-w-[100px]">{lead.lastAction}</div>
|
|
198
|
-
{lead.assignedTo && (
|
|
199
|
-
<div className="text-[8px] text-green-400 flex items-center justify-end gap-1 mt-0.5">
|
|
200
|
-
<Zap size={8} /> {lead.assignedTo}
|
|
201
|
-
</div>
|
|
202
|
-
)}
|
|
203
|
-
</div>
|
|
204
|
-
</div>
|
|
205
|
-
{/* Score Bar */}
|
|
206
|
-
<div className="mt-1.5 h-1 bg-slate-900 rounded-full overflow-hidden">
|
|
207
|
-
<div
|
|
208
|
-
className="h-full rounded-full transition-all duration-700"
|
|
209
|
-
style={{ width: `${lead.score}%`, background: cfg.color }}
|
|
210
|
-
/>
|
|
211
|
-
</div>
|
|
212
|
-
</div>
|
|
213
|
-
);
|
|
214
|
-
})}
|
|
215
|
-
</div>
|
|
216
|
-
|
|
217
|
-
{/* Footer */}
|
|
218
|
-
<div className="flex items-center justify-between text-[10px] text-slate-500 pt-1 border-t border-slate-800">
|
|
219
|
-
<span>Conv. Rate: <span className="text-green-400 font-bold">3.4%</span></span>
|
|
220
|
-
<span>Avg Score: <span className="text-blue-400 font-bold">{Math.round(leads.reduce((s, l) => s + l.score, 0) / leads.length)}</span></span>
|
|
221
|
-
<span>Auto-asignados: <span className="text-purple-400 font-bold">{leads.filter(l => l.assignedTo).length}</span></span>
|
|
222
|
-
</div>
|
|
223
|
-
</div>
|
|
224
|
-
</BaseNode>
|
|
225
|
-
);
|
|
226
|
-
};
|