@decido/shell 1.0.0 → 4.0.2
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 +27 -18
- 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/index.ts +0 -97
- 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,205 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import { BaseNode } from '@decido/macia-core';
|
|
3
|
-
import {
|
|
4
|
-
Car, TrendingUp, TrendingDown, MapPin, BarChart3,
|
|
5
|
-
Package, Palette, Calendar
|
|
6
|
-
} from 'lucide-react';
|
|
7
|
-
|
|
8
|
-
/* ═══════════════════════════════════════════════════════════════
|
|
9
|
-
KIA PBI RUNT NODE — Mirror de PowerBI "RUNT_CONCESIONARIOS"
|
|
10
|
-
Datos REALES Grecco Motors · Febrero 2026
|
|
11
|
-
═══════════════════════════════════════════════════════════════ */
|
|
12
|
-
|
|
13
|
-
const RUNT_SUMMARY = {
|
|
14
|
-
total: 95, particular: 94, taxi: 1,
|
|
15
|
-
auto: 60, suv: 35,
|
|
16
|
-
ice: 86, hibrido: 9,
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const MODELS = [
|
|
20
|
-
{ model: 'Picanto', units: 46, pct: 48.4, color: '#ef4444' },
|
|
21
|
-
{ model: 'K3 Cross', units: 12, pct: 12.6, color: '#f59e0b' },
|
|
22
|
-
{ model: 'Sportage', units: 10, pct: 10.5, color: '#22c55e' },
|
|
23
|
-
{ model: 'K3 Sedan', units: 8, pct: 8.4, color: '#3b82f6' },
|
|
24
|
-
{ model: 'Soluto', units: 6, pct: 6.3, color: '#8b5cf6' },
|
|
25
|
-
{ model: 'Stonic', units: 5, pct: 5.3, color: '#06b6d4' },
|
|
26
|
-
{ model: 'Otros', units: 8, pct: 8.4, color: '#64748b' },
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
const VARIATION = [
|
|
30
|
-
{ period: 'Feb 2026', units: 95, vs: null },
|
|
31
|
-
{ period: 'Ene 2026', units: 147, vs: -35 },
|
|
32
|
-
{ period: 'Feb 2025', units: 101, vs: -6 },
|
|
33
|
-
{ period: 'Dic 2025', units: 182, vs: null },
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
const INVENTORY = [
|
|
37
|
-
{ model: 'Stonic', qty: 15, year: '2027', color: '#22c55e' },
|
|
38
|
-
{ model: 'Sportage', qty: 12, year: '2027', color: '#3b82f6' },
|
|
39
|
-
{ model: 'Soluto', qty: 10, year: '2026', color: '#f59e0b' },
|
|
40
|
-
{ model: 'Sonet', qty: 9, year: '2027', color: '#8b5cf6' },
|
|
41
|
-
{ model: 'Tasman', qty: 6, year: '2027', color: '#ef4444' },
|
|
42
|
-
{ model: 'EV5', qty: 4, year: '2027', color: '#06b6d4' },
|
|
43
|
-
{ model: 'Picanto', qty: 8, year: '2026', color: '#f97316' },
|
|
44
|
-
];
|
|
45
|
-
|
|
46
|
-
const CITIES = [
|
|
47
|
-
{ city: 'Girón', units: 41 },
|
|
48
|
-
{ city: 'Villa del Rosario', units: 27 },
|
|
49
|
-
{ city: 'Cúcuta', units: 9 },
|
|
50
|
-
{ city: 'Bucaramanga', units: 6 },
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
const TOP_COLORS = [
|
|
54
|
-
{ color: 'Gris', pct: 32 },
|
|
55
|
-
{ color: 'Blanco', pct: 28 },
|
|
56
|
-
{ color: 'Plata', pct: 18 },
|
|
57
|
-
{ color: 'Negro', pct: 12 },
|
|
58
|
-
{ color: 'Rojo', pct: 10 },
|
|
59
|
-
];
|
|
60
|
-
|
|
61
|
-
export const KiaPBIRuntNode = (props: any) => {
|
|
62
|
-
const [tab, setTab] = useState<'registro' | 'inventario' | 'variacion'>('registro');
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<BaseNode {...props} title="RUNT Grecco — PBI" width={520} height={640}>
|
|
66
|
-
<div className="flex flex-col h-full bg-[#1a1a2e] text-white overflow-hidden">
|
|
67
|
-
|
|
68
|
-
{/* Header */}
|
|
69
|
-
<div className="flex items-center gap-3 p-3 bg-[#16213e] border-b border-cyan-900/40">
|
|
70
|
-
<div className="w-9 h-9 rounded bg-cyan-600/20 flex items-center justify-center">
|
|
71
|
-
<Car size={16} className="text-cyan-400" />
|
|
72
|
-
</div>
|
|
73
|
-
<div className="flex-1">
|
|
74
|
-
<div className="text-xs font-bold tracking-wide flex items-center gap-2">
|
|
75
|
-
RUNT CONCESIONARIOS
|
|
76
|
-
<span className="text-[8px] px-1.5 py-0.5 bg-yellow-500/20 text-yellow-400 rounded">FEB 2026</span>
|
|
77
|
-
<span className="text-[8px] px-1.5 py-0.5 bg-cyan-500/20 text-cyan-400 rounded">POWER BI</span>
|
|
78
|
-
</div>
|
|
79
|
-
<div className="text-[10px] text-slate-400">Grecco Motors · Santander / Norte de Santander</div>
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
|
|
83
|
-
{/* KPI Row */}
|
|
84
|
-
<div className="flex gap-1 px-3 py-2 border-b border-cyan-900/20">
|
|
85
|
-
{[
|
|
86
|
-
{ label: 'Total', val: '95', color: 'text-cyan-400' },
|
|
87
|
-
{ label: 'Automóvil', val: '60', color: 'text-blue-400' },
|
|
88
|
-
{ label: 'SUV', val: '35', color: 'text-green-400' },
|
|
89
|
-
{ label: 'ICE', val: '86', color: 'text-yellow-400' },
|
|
90
|
-
{ label: 'Híbrido', val: '9', color: 'text-purple-400' },
|
|
91
|
-
].map(k => (
|
|
92
|
-
<div key={k.label} className="flex-1 text-center bg-[#16213e]/50 rounded p-1.5 border border-cyan-900/10">
|
|
93
|
-
<div className={`text-sm font-bold ${k.color}`}>{k.val}</div>
|
|
94
|
-
<div className="text-[7px] text-slate-500">{k.label}</div>
|
|
95
|
-
</div>
|
|
96
|
-
))}
|
|
97
|
-
</div>
|
|
98
|
-
|
|
99
|
-
{/* Tabs */}
|
|
100
|
-
<div className="flex gap-1 p-2 bg-[#16213e]/30 border-b border-cyan-900/20">
|
|
101
|
-
{([
|
|
102
|
-
{ key: 'registro' as const, label: '📊 Registro' },
|
|
103
|
-
{ key: 'inventario' as const, label: '📦 Inventario' },
|
|
104
|
-
{ key: 'variacion' as const, label: '📈 Variación' },
|
|
105
|
-
]).map(t => (
|
|
106
|
-
<button key={t.key} onClick={() => setTab(t.key)}
|
|
107
|
-
className={`flex-1 py-1.5 rounded text-[9px] font-bold transition-all ${tab === t.key ? 'bg-cyan-600/20 text-cyan-300 border border-cyan-500/30' : 'text-slate-500'
|
|
108
|
-
}`}>
|
|
109
|
-
{t.label}
|
|
110
|
-
</button>
|
|
111
|
-
))}
|
|
112
|
-
</div>
|
|
113
|
-
|
|
114
|
-
<div className="flex-1 overflow-y-auto p-3 space-y-1.5">
|
|
115
|
-
|
|
116
|
-
{tab === 'registro' && (
|
|
117
|
-
<>
|
|
118
|
-
<div className="text-[9px] text-slate-500 font-bold uppercase mb-1">Matrículas por Modelo</div>
|
|
119
|
-
{MODELS.map(m => (
|
|
120
|
-
<div key={m.model} className="bg-[#16213e]/40 rounded p-2 border border-cyan-900/10"
|
|
121
|
-
style={{ borderLeftWidth: '3px', borderLeftColor: m.color }}>
|
|
122
|
-
<div className="flex items-center justify-between">
|
|
123
|
-
<span className="text-[10px] font-bold">{m.model}</span>
|
|
124
|
-
<span className="text-[10px] font-bold" style={{ color: m.color }}>{m.units} uds</span>
|
|
125
|
-
</div>
|
|
126
|
-
<div className="h-1.5 bg-[#0a0f1a] rounded-full mt-1">
|
|
127
|
-
<div className="h-full rounded-full" style={{ width: `${m.pct * 2}%`, background: m.color }} />
|
|
128
|
-
</div>
|
|
129
|
-
</div>
|
|
130
|
-
))}
|
|
131
|
-
<div className="text-[9px] text-slate-500 font-bold uppercase mt-2 mb-1">Distribución Geográfica</div>
|
|
132
|
-
{CITIES.map(c => (
|
|
133
|
-
<div key={c.city} className="bg-[#16213e]/40 rounded p-1.5 flex items-center justify-between border border-cyan-900/10">
|
|
134
|
-
<span className="text-[9px]">📍 {c.city}</span>
|
|
135
|
-
<span className="text-[10px] font-bold text-cyan-400">{c.units} uds</span>
|
|
136
|
-
</div>
|
|
137
|
-
))}
|
|
138
|
-
</>
|
|
139
|
-
)}
|
|
140
|
-
|
|
141
|
-
{tab === 'inventario' && (
|
|
142
|
-
<>
|
|
143
|
-
<div className="text-[9px] text-slate-500 font-bold uppercase mb-1">Inventario Libre — Feb 14, 2026</div>
|
|
144
|
-
{INVENTORY.map(inv => (
|
|
145
|
-
<div key={inv.model} className="bg-[#16213e]/40 rounded p-2 border border-cyan-900/10"
|
|
146
|
-
style={{ borderLeftWidth: '3px', borderLeftColor: inv.color }}>
|
|
147
|
-
<div className="flex items-center justify-between">
|
|
148
|
-
<div>
|
|
149
|
-
<span className="text-[10px] font-bold">{inv.model}</span>
|
|
150
|
-
<span className="text-[8px] text-slate-500 ml-1">MY{inv.year}</span>
|
|
151
|
-
</div>
|
|
152
|
-
<span className="text-[10px] font-bold" style={{ color: inv.color }}>{inv.qty}</span>
|
|
153
|
-
</div>
|
|
154
|
-
</div>
|
|
155
|
-
))}
|
|
156
|
-
<div className="text-[9px] text-slate-500 font-bold uppercase mt-2 mb-1">Colores en Stock</div>
|
|
157
|
-
<div className="flex gap-1">
|
|
158
|
-
{TOP_COLORS.map(c => (
|
|
159
|
-
<div key={c.color} className="flex-1 text-center bg-[#16213e]/50 rounded p-1.5 border border-cyan-900/10">
|
|
160
|
-
<div className="text-[10px] font-bold text-white">{c.pct}%</div>
|
|
161
|
-
<div className="text-[7px] text-slate-500">{c.color}</div>
|
|
162
|
-
</div>
|
|
163
|
-
))}
|
|
164
|
-
</div>
|
|
165
|
-
</>
|
|
166
|
-
)}
|
|
167
|
-
|
|
168
|
-
{tab === 'variacion' && (
|
|
169
|
-
<>
|
|
170
|
-
<div className="text-[9px] text-slate-500 font-bold uppercase mb-1">Variación RUNT</div>
|
|
171
|
-
{VARIATION.filter(v => v.vs !== null).map(v => (
|
|
172
|
-
<div key={v.period} className="bg-[#16213e]/40 rounded-lg p-3 border border-cyan-900/10">
|
|
173
|
-
<div className="flex items-center justify-between mb-1">
|
|
174
|
-
<span className="text-[10px] font-bold">{v.period}</span>
|
|
175
|
-
<span className="text-sm font-bold text-white">{v.units} uds</span>
|
|
176
|
-
</div>
|
|
177
|
-
<div className="flex items-center gap-2">
|
|
178
|
-
<span className={`text-[10px] font-bold ${v.vs! < 0 ? 'text-red-400' : 'text-green-400'}`}>
|
|
179
|
-
{v.vs! > 0 ? '↑' : '↓'}{Math.abs(v.vs!)}% vs Feb 2026
|
|
180
|
-
</span>
|
|
181
|
-
</div>
|
|
182
|
-
</div>
|
|
183
|
-
))}
|
|
184
|
-
<div className="bg-[#16213e]/40 rounded-lg p-3 border border-cyan-900/10 mt-2">
|
|
185
|
-
<div className="text-[9px] text-slate-500 font-bold uppercase mb-1">Tendencia Trimestral</div>
|
|
186
|
-
<div className="flex gap-1 items-end h-16">
|
|
187
|
-
{[{ m: 'Dic', v: 182 }, { m: 'Ene', v: 147 }, { m: 'Feb', v: 95 }].map(d => (
|
|
188
|
-
<div key={d.m} className="flex-1 flex flex-col items-center">
|
|
189
|
-
<span className="text-[8px] font-bold text-cyan-400 mb-0.5">{d.v}</span>
|
|
190
|
-
<div className="w-full bg-cyan-500/60 rounded-t" style={{ height: `${(d.v / 182) * 100}%` }} />
|
|
191
|
-
<span className="text-[7px] text-slate-500 mt-0.5">{d.m}</span>
|
|
192
|
-
</div>
|
|
193
|
-
))}
|
|
194
|
-
</div>
|
|
195
|
-
</div>
|
|
196
|
-
<div className="bg-red-500/10 rounded-lg p-2 border border-red-500/20 text-[9px] text-red-400 mt-1">
|
|
197
|
-
⚠️ Tendencia decreciente: -35% MoM · -6% YoY. Market Share: Auto 2.9%, SUV 0.7%
|
|
198
|
-
</div>
|
|
199
|
-
</>
|
|
200
|
-
)}
|
|
201
|
-
</div>
|
|
202
|
-
</div>
|
|
203
|
-
</BaseNode>
|
|
204
|
-
);
|
|
205
|
-
};
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { BaseNode } from '@decido/macia-core';
|
|
3
|
-
import {
|
|
4
|
-
Star, Award, TrendingUp, Target, Shield,
|
|
5
|
-
Users, Wrench, Heart, DollarSign, Laptop, ChevronUp, ChevronDown
|
|
6
|
-
} from 'lucide-react';
|
|
7
|
-
|
|
8
|
-
/* ═══════════════════════════════════════════════════════════════
|
|
9
|
-
KIA PARTNER SCORE NODE — Partner Program Scoring · Grecco Motors
|
|
10
|
-
Radar chart 5 ejes, scoring automatizado, trends
|
|
11
|
-
═══════════════════════════════════════════════════════════════ */
|
|
12
|
-
|
|
13
|
-
const AREAS = [
|
|
14
|
-
{ key: 'ventas', label: 'Comercial', weight: 20, score: 95, prev: 92, icon: TrendingUp, color: '#3b82f6' },
|
|
15
|
-
{ key: 'posventa', label: 'Servicio', weight: 20, score: 100, prev: 98, icon: Wrench, color: '#22c55e' },
|
|
16
|
-
{ key: 'cx', label: 'Customer XP', weight: 15, score: 100, prev: 97, icon: Heart, color: '#a855f7' },
|
|
17
|
-
{ key: 'financiero', label: 'Business Mgmt', weight: 15, score: 100, prev: 96, icon: DollarSign, color: '#f59e0b' },
|
|
18
|
-
{ key: 'digital', label: 'Transf. Digital', weight: 10, score: 100, prev: 98, icon: Laptop, color: '#06b6d4' },
|
|
19
|
-
{ key: 'marketing', label: 'Marketing', weight: 10, score: 99, prev: 97, icon: Target, color: '#e040fb' },
|
|
20
|
-
{ key: 'repuestos', label: 'Repuestos', weight: 10, score: 92, prev: 89, icon: Shield, color: '#f97316' },
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
const RANKINGS = ['S+', 'S', 'A', 'B', 'C', 'D'] as const;
|
|
24
|
-
|
|
25
|
-
const ACTION_ITEMS = [
|
|
26
|
-
{ area: 'Digital', action: 'Completar módulo Academy — Finance Pro', impact: '+3 pts', deadline: 'Mar 15' },
|
|
27
|
-
{ area: 'Financiero', action: 'Aumentar penetración seguros a 75%', impact: '+4 pts', deadline: 'Mar 30' },
|
|
28
|
-
{ area: 'Ventas', action: 'Mejorar lead time < 2h para leads Hot', impact: '+2 pts', deadline: 'Feb 28' },
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
export const KiaPartnerScoreNode = (props: any) => {
|
|
32
|
-
const [scores, setScores] = useState(AREAS.map(a => ({ ...a })));
|
|
33
|
-
const [animPhase, setAnimPhase] = useState(0);
|
|
34
|
-
|
|
35
|
-
const totalScore = Math.round(scores.reduce((s, a) => s + (a.score * a.weight / 100), 0));
|
|
36
|
-
const prevTotal = Math.round(scores.reduce((s, a) => s + (a.prev * a.weight / 100), 0));
|
|
37
|
-
const ranking = totalScore >= 92 ? 'S+' : totalScore >= 85 ? 'S' : totalScore >= 75 ? 'A' : totalScore >= 65 ? 'B' : totalScore >= 50 ? 'C' : 'D';
|
|
38
|
-
const prevRanking = prevTotal >= 92 ? 'S+' : prevTotal >= 85 ? 'S' : prevTotal >= 75 ? 'A' : prevTotal >= 65 ? 'B' : prevTotal >= 50 ? 'C' : 'D';
|
|
39
|
-
|
|
40
|
-
// Radar chart math
|
|
41
|
-
const cx = 100, cy = 100, r = 70;
|
|
42
|
-
const n = scores.length;
|
|
43
|
-
const polygonPoints = scores.map((a, i) => {
|
|
44
|
-
const angle = (Math.PI * 2 * i) / n - Math.PI / 2;
|
|
45
|
-
const radius = (a.score / 100) * r;
|
|
46
|
-
return `${cx + radius * Math.cos(angle)},${cy + radius * Math.sin(angle)}`;
|
|
47
|
-
}).join(' ');
|
|
48
|
-
const prevPolygon = scores.map((a, i) => {
|
|
49
|
-
const angle = (Math.PI * 2 * i) / n - Math.PI / 2;
|
|
50
|
-
const radius = (a.prev / 100) * r;
|
|
51
|
-
return `${cx + radius * Math.cos(angle)},${cy + radius * Math.sin(angle)}`;
|
|
52
|
-
}).join(' ');
|
|
53
|
-
const gridLevels = [25, 50, 75, 100];
|
|
54
|
-
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
const t = setTimeout(() => setAnimPhase(1), 500);
|
|
57
|
-
return () => clearTimeout(t);
|
|
58
|
-
}, []);
|
|
59
|
-
|
|
60
|
-
// Subtle score jitter
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
const interval = setInterval(() => {
|
|
63
|
-
setScores(prev => prev.map(a => ({
|
|
64
|
-
...a,
|
|
65
|
-
score: Math.min(100, Math.max(60, a.score + Math.floor(Math.random() * 3 - 1))),
|
|
66
|
-
})));
|
|
67
|
-
}, 8000);
|
|
68
|
-
return () => clearInterval(interval);
|
|
69
|
-
}, []);
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<BaseNode {...props} title="Kia Partner Program" width={480} height={620}>
|
|
73
|
-
<div className="flex flex-col h-full bg-slate-950 text-white overflow-hidden">
|
|
74
|
-
|
|
75
|
-
{/* Header */}
|
|
76
|
-
<div className="flex items-center gap-3 p-3 bg-slate-900 border-b border-slate-800">
|
|
77
|
-
<div className="w-9 h-9 rounded-full bg-red-600/20 flex items-center justify-center border border-red-500/30">
|
|
78
|
-
<span className="text-red-500 font-bold text-[7px] tracking-widest">KIA</span>
|
|
79
|
-
</div>
|
|
80
|
-
<div className="flex-1">
|
|
81
|
-
<div className="text-xs font-bold tracking-wide">PARTNER PROGRAM</div>
|
|
82
|
-
<div className="text-[10px] text-slate-400">Grecco Motors · Scoring 360° · Ranking {ranking}</div>
|
|
83
|
-
</div>
|
|
84
|
-
<div className="flex items-center gap-2">
|
|
85
|
-
<div className={`px-3 py-1.5 rounded-lg text-base font-black ${ranking === 'S+' ? 'bg-yellow-500/20 text-yellow-400 border border-yellow-500/30' :
|
|
86
|
-
ranking === 'S' ? 'bg-green-500/20 text-green-400 border border-green-500/30' :
|
|
87
|
-
'bg-blue-500/20 text-blue-400 border border-blue-500/30'
|
|
88
|
-
}`}>
|
|
89
|
-
{ranking}
|
|
90
|
-
</div>
|
|
91
|
-
<div className="text-right">
|
|
92
|
-
<div className="text-lg font-bold">{totalScore}</div>
|
|
93
|
-
<div className={`text-[9px] flex items-center gap-0.5 ${totalScore > prevTotal ? 'text-green-400' : 'text-red-400'}`}>
|
|
94
|
-
{totalScore > prevTotal ? <ChevronUp size={9} /> : <ChevronDown size={9} />}
|
|
95
|
-
{Math.abs(totalScore - prevTotal)} pts
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
|
|
101
|
-
<div className="flex-1 overflow-y-auto p-3 space-y-3">
|
|
102
|
-
|
|
103
|
-
{/* Radar Chart */}
|
|
104
|
-
<div className="flex gap-3">
|
|
105
|
-
<div className="flex-1">
|
|
106
|
-
<svg viewBox="0 0 200 200" className="w-full">
|
|
107
|
-
{/* Grid circles */}
|
|
108
|
-
{gridLevels.map(level => (
|
|
109
|
-
<polygon key={level}
|
|
110
|
-
points={Array.from({ length: n }, (_, i) => {
|
|
111
|
-
const angle = (Math.PI * 2 * i) / n - Math.PI / 2;
|
|
112
|
-
const radius = (level / 100) * r;
|
|
113
|
-
return `${cx + radius * Math.cos(angle)},${cy + radius * Math.sin(angle)}`;
|
|
114
|
-
}).join(' ')}
|
|
115
|
-
fill="none" stroke="rgba(100,116,139,0.15)" strokeWidth="0.5"
|
|
116
|
-
/>
|
|
117
|
-
))}
|
|
118
|
-
{/* Axes */}
|
|
119
|
-
{scores.map((a, i) => {
|
|
120
|
-
const angle = (Math.PI * 2 * i) / n - Math.PI / 2;
|
|
121
|
-
return (
|
|
122
|
-
<React.Fragment key={a.key}>
|
|
123
|
-
<line x1={cx} y1={cy} x2={cx + r * Math.cos(angle)} y2={cy + r * Math.sin(angle)}
|
|
124
|
-
stroke="rgba(100,116,139,0.1)" strokeWidth="0.5" />
|
|
125
|
-
<text x={cx + (r + 14) * Math.cos(angle)} y={cy + (r + 14) * Math.sin(angle)}
|
|
126
|
-
fill={a.color} fontSize="7" fontWeight="600" textAnchor="middle" dominantBaseline="middle">
|
|
127
|
-
{a.label}
|
|
128
|
-
</text>
|
|
129
|
-
</React.Fragment>
|
|
130
|
-
);
|
|
131
|
-
})}
|
|
132
|
-
{/* Previous period */}
|
|
133
|
-
<polygon points={prevPolygon} fill="rgba(100,116,139,0.06)" stroke="rgba(100,116,139,0.3)" strokeWidth="1" strokeDasharray="3,2" />
|
|
134
|
-
{/* Current */}
|
|
135
|
-
<polygon points={polygonPoints} fill="rgba(239,68,68,0.1)" stroke="#ef4444" strokeWidth="1.5"
|
|
136
|
-
style={{ transition: 'all 1s ease' }} />
|
|
137
|
-
{/* Score points */}
|
|
138
|
-
{scores.map((a, i) => {
|
|
139
|
-
const angle = (Math.PI * 2 * i) / n - Math.PI / 2;
|
|
140
|
-
const radius = (a.score / 100) * r;
|
|
141
|
-
return <circle key={a.key} cx={cx + radius * Math.cos(angle)} cy={cy + radius * Math.sin(angle)}
|
|
142
|
-
r="3" fill={a.color} stroke="#0f172a" strokeWidth="1" />;
|
|
143
|
-
})}
|
|
144
|
-
</svg>
|
|
145
|
-
</div>
|
|
146
|
-
|
|
147
|
-
{/* Breakdown */}
|
|
148
|
-
<div className="w-[45%] space-y-1.5">
|
|
149
|
-
{scores.map(a => (
|
|
150
|
-
<div key={a.key} className="bg-slate-900 rounded-lg p-1.5 border border-slate-800">
|
|
151
|
-
<div className="flex items-center justify-between text-[10px] mb-0.5">
|
|
152
|
-
<div className="flex items-center gap-1">
|
|
153
|
-
<a.icon size={10} style={{ color: a.color }} />
|
|
154
|
-
<span className="font-semibold">{a.label}</span>
|
|
155
|
-
</div>
|
|
156
|
-
<div className="flex items-center gap-1">
|
|
157
|
-
<span className="font-bold" style={{ color: a.color }}>{a.score}</span>
|
|
158
|
-
<span className="text-[8px] text-slate-500">/ {a.weight}%</span>
|
|
159
|
-
</div>
|
|
160
|
-
</div>
|
|
161
|
-
<div className="w-full h-1 bg-slate-800 rounded-full">
|
|
162
|
-
<div className="h-full rounded-full transition-all duration-700" style={{ width: `${a.score}%`, background: a.color }} />
|
|
163
|
-
</div>
|
|
164
|
-
<div className={`text-[8px] mt-0.5 ${a.score > a.prev ? 'text-green-400' : 'text-red-400'}`}>
|
|
165
|
-
{a.score > a.prev ? '↑' : '↓'} {Math.abs(a.score - a.prev)} vs. anterior
|
|
166
|
-
</div>
|
|
167
|
-
</div>
|
|
168
|
-
))}
|
|
169
|
-
</div>
|
|
170
|
-
</div>
|
|
171
|
-
|
|
172
|
-
{/* Action Items */}
|
|
173
|
-
<div>
|
|
174
|
-
<div className="text-[9px] text-slate-500 font-bold uppercase mb-1.5 flex items-center gap-1"><Target size={10} /> Plan de Mejora — Quick Wins</div>
|
|
175
|
-
<div className="space-y-1">
|
|
176
|
-
{ACTION_ITEMS.map((item, i) => (
|
|
177
|
-
<div key={i} className="flex items-center gap-2 bg-slate-900 rounded-lg p-2 border border-slate-800">
|
|
178
|
-
<div className="flex-1 min-w-0">
|
|
179
|
-
<div className="text-[10px] text-slate-300">{item.action}</div>
|
|
180
|
-
<div className="text-[9px] text-slate-500">{item.area} · {item.deadline}</div>
|
|
181
|
-
</div>
|
|
182
|
-
<span className="text-[9px] font-bold text-green-400 whitespace-nowrap">{item.impact}</span>
|
|
183
|
-
</div>
|
|
184
|
-
))}
|
|
185
|
-
</div>
|
|
186
|
-
</div>
|
|
187
|
-
|
|
188
|
-
{/* Ranking Scale */}
|
|
189
|
-
<div className="bg-slate-900 rounded-lg p-2 border border-slate-800">
|
|
190
|
-
<div className="text-[9px] text-slate-500 font-bold uppercase mb-1.5">Escala Partner Program</div>
|
|
191
|
-
<div className="flex gap-1">
|
|
192
|
-
{RANKINGS.map(r => (
|
|
193
|
-
<div key={r} className={`flex-1 text-center py-1 rounded text-[10px] font-bold ${r === ranking
|
|
194
|
-
? 'bg-red-500/20 text-red-400 border border-red-500/30'
|
|
195
|
-
: 'bg-slate-800 text-slate-500'
|
|
196
|
-
}`}>
|
|
197
|
-
{r}
|
|
198
|
-
</div>
|
|
199
|
-
))}
|
|
200
|
-
</div>
|
|
201
|
-
</div>
|
|
202
|
-
</div>
|
|
203
|
-
</div>
|
|
204
|
-
</BaseNode>
|
|
205
|
-
);
|
|
206
|
-
};
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { BaseNode } from '@decido/macia-core';
|
|
3
|
-
import {
|
|
4
|
-
Brain, TrendingUp, AlertTriangle, BarChart3, Car,
|
|
5
|
-
Users, ShoppingCart, Wrench, ChevronUp, ChevronDown, Sparkles, Target
|
|
6
|
-
} from 'lucide-react';
|
|
7
|
-
|
|
8
|
-
/* ═══════════════════════════════════════════════════════════════
|
|
9
|
-
KIA PREDICTIVE ANALYTICS NODE — ML Models · Grecco Motors
|
|
10
|
-
Demand Forecast, Churn Prevention, Lead Conversion, Parts Demand
|
|
11
|
-
═══════════════════════════════════════════════════════════════ */
|
|
12
|
-
|
|
13
|
-
interface Prediction {
|
|
14
|
-
model: string;
|
|
15
|
-
forecast: number;
|
|
16
|
-
trend: number;
|
|
17
|
-
confidence: number;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const DEMAND_FORECAST: Prediction[] = [
|
|
21
|
-
{ model: 'Sportage', forecast: 42, trend: 8, confidence: 94 },
|
|
22
|
-
{ model: 'Seltos', forecast: 28, trend: 3, confidence: 91 },
|
|
23
|
-
{ model: 'EV6', forecast: 18, trend: 15, confidence: 87 },
|
|
24
|
-
{ model: 'Picanto', forecast: 35, trend: -2, confidence: 92 },
|
|
25
|
-
{ model: 'K3 Cross', forecast: 22, trend: 5, confidence: 89 },
|
|
26
|
-
{ model: 'Sorento', forecast: 12, trend: 1, confidence: 85 },
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
const CHURN_RISKS = [
|
|
30
|
-
{ name: 'Pedro Martínez', model: 'Sportage 2022', risk: 82, reason: 'NPS 45 + Sin servicio 14 meses', action: 'Llamada CX urgente' },
|
|
31
|
-
{ name: 'Ana Restrepo', model: 'Seltos 2023', risk: 67, reason: 'Reclamación garantía sin resolver', action: 'Escalación Posventa' },
|
|
32
|
-
{ name: 'Diego Arias', model: 'K3 2021', risk: 54, reason: 'Fin garantía próximo + sin contacto', action: 'Campaña retoma' },
|
|
33
|
-
];
|
|
34
|
-
|
|
35
|
-
const LEAD_CONVERSION = [
|
|
36
|
-
{ source: 'Facebook Ads', leads: 156, predicted: 18, rate: 11.5, trend: 2.1 },
|
|
37
|
-
{ source: 'Google Search', leads: 89, predicted: 14, rate: 15.7, trend: -0.3 },
|
|
38
|
-
{ source: 'Walk-in', leads: 67, predicted: 22, rate: 32.8, trend: 1.4 },
|
|
39
|
-
{ source: 'Referral', leads: 34, predicted: 12, rate: 35.3, trend: 3.2 },
|
|
40
|
-
{ source: 'WhatsApp Bot', leads: 91, predicted: 9, rate: 9.9, trend: 4.5 },
|
|
41
|
-
];
|
|
42
|
-
|
|
43
|
-
const PARTS_FORECAST = [
|
|
44
|
-
{ part: 'Filtro Aceite Sportage', current: 34, predicted: 52, reorder: true, daysToStockout: 8 },
|
|
45
|
-
{ part: 'Pastillas Freno Seltos', current: 12, predicted: 28, reorder: true, daysToStockout: 3 },
|
|
46
|
-
{ part: 'Bujías K3 NGK', current: 89, predicted: 15, reorder: false, daysToStockout: 45 },
|
|
47
|
-
{ part: 'Batería 12V EV6 Aux', current: 6, predicted: 8, reorder: true, daysToStockout: 5 },
|
|
48
|
-
{ part: 'Amortiguadores Sorento', current: 22, predicted: 10, reorder: false, daysToStockout: 18 },
|
|
49
|
-
];
|
|
50
|
-
|
|
51
|
-
export const KiaPredictiveNode = (props: any) => {
|
|
52
|
-
const [tab, setTab] = useState<'demand' | 'churn' | 'leads' | 'parts'>('demand');
|
|
53
|
-
const [forecast, setForecast] = useState(DEMAND_FORECAST.map(d => ({ ...d })));
|
|
54
|
-
|
|
55
|
-
// Subtle jitter on forecasts
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
const interval = setInterval(() => {
|
|
58
|
-
setForecast(prev => prev.map(d => ({
|
|
59
|
-
...d,
|
|
60
|
-
forecast: d.forecast + Math.floor(Math.random() * 3 - 1),
|
|
61
|
-
confidence: Math.min(99, Math.max(80, d.confidence + Math.floor(Math.random() * 3 - 1))),
|
|
62
|
-
})));
|
|
63
|
-
}, 6000);
|
|
64
|
-
return () => clearInterval(interval);
|
|
65
|
-
}, []);
|
|
66
|
-
|
|
67
|
-
const totalForecast = forecast.reduce((s, d) => s + d.forecast, 0);
|
|
68
|
-
|
|
69
|
-
return (
|
|
70
|
-
<BaseNode {...props} title="Kia Predictive Analytics" width={500} height={620}>
|
|
71
|
-
<div className="flex flex-col h-full bg-slate-950 text-white overflow-hidden">
|
|
72
|
-
|
|
73
|
-
{/* Header */}
|
|
74
|
-
<div className="flex items-center gap-3 p-3 bg-slate-900 border-b border-slate-800">
|
|
75
|
-
<div className="w-9 h-9 rounded-full bg-purple-600/20 flex items-center justify-center border border-purple-500/30">
|
|
76
|
-
<Brain size={16} className="text-purple-400" />
|
|
77
|
-
</div>
|
|
78
|
-
<div className="flex-1">
|
|
79
|
-
<div className="text-xs font-bold tracking-wide flex items-center gap-2">
|
|
80
|
-
PREDICTIVE AI
|
|
81
|
-
<span className="text-[8px] px-1.5 py-0.5 bg-purple-500/20 text-purple-400 rounded font-normal">4 MODELOS</span>
|
|
82
|
-
</div>
|
|
83
|
-
<div className="text-[10px] text-slate-400">Grecco Motors · Machine Learning · {totalForecast} unid/mes forecast</div>
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
|
|
87
|
-
{/* Tabs */}
|
|
88
|
-
<div className="flex gap-1 p-2 bg-slate-900/50 border-b border-slate-800">
|
|
89
|
-
{([
|
|
90
|
-
{ key: 'demand' as const, icon: BarChart3, label: 'Demanda' },
|
|
91
|
-
{ key: 'churn' as const, icon: AlertTriangle, label: 'Churn' },
|
|
92
|
-
{ key: 'leads' as const, icon: Users, label: 'Conversión' },
|
|
93
|
-
{ key: 'parts' as const, icon: ShoppingCart, label: 'Repuestos' },
|
|
94
|
-
]).map(t => (
|
|
95
|
-
<button key={t.key} onClick={() => setTab(t.key)}
|
|
96
|
-
className={`flex-1 flex items-center justify-center gap-1 py-1.5 rounded text-[9px] font-bold transition-all ${tab === t.key ? 'bg-purple-600/20 text-purple-400 border border-purple-500/30' : 'text-slate-500'
|
|
97
|
-
}`}>
|
|
98
|
-
<t.icon size={10} /> {t.label}
|
|
99
|
-
</button>
|
|
100
|
-
))}
|
|
101
|
-
</div>
|
|
102
|
-
|
|
103
|
-
{/* Content */}
|
|
104
|
-
<div className="flex-1 overflow-y-auto p-3 space-y-2">
|
|
105
|
-
|
|
106
|
-
{/* Demand Forecast */}
|
|
107
|
-
{tab === 'demand' && (
|
|
108
|
-
<>
|
|
109
|
-
<div className="text-[9px] text-slate-500 font-bold uppercase flex items-center gap-1 mb-1">
|
|
110
|
-
<Sparkles size={10} className="text-purple-400" /> Forecast Próximo Mes — Por Modelo
|
|
111
|
-
</div>
|
|
112
|
-
{forecast.map(d => (
|
|
113
|
-
<div key={d.model} className="bg-slate-900/50 rounded-lg p-2.5 border border-slate-800">
|
|
114
|
-
<div className="flex items-center justify-between mb-1">
|
|
115
|
-
<div className="flex items-center gap-2">
|
|
116
|
-
<Car size={12} className="text-slate-400" />
|
|
117
|
-
<span className="text-[11px] font-bold">{d.model}</span>
|
|
118
|
-
</div>
|
|
119
|
-
<div className="flex items-center gap-2">
|
|
120
|
-
<span className={`text-[9px] flex items-center gap-0.5 ${d.trend > 0 ? 'text-green-400' : d.trend < 0 ? 'text-red-400' : 'text-slate-400'}`}>
|
|
121
|
-
{d.trend > 0 ? <ChevronUp size={9} /> : <ChevronDown size={9} />}
|
|
122
|
-
{Math.abs(d.trend)}%
|
|
123
|
-
</span>
|
|
124
|
-
<span className="text-sm font-bold text-blue-400">{d.forecast}</span>
|
|
125
|
-
</div>
|
|
126
|
-
</div>
|
|
127
|
-
<div className="flex items-center gap-2">
|
|
128
|
-
<div className="flex-1 h-1.5 bg-slate-800 rounded-full">
|
|
129
|
-
<div className="h-full rounded-full bg-linear-to-r from-blue-500 to-purple-500 transition-all duration-700"
|
|
130
|
-
style={{ width: `${Math.min(100, (d.forecast / 50) * 100)}%` }} />
|
|
131
|
-
</div>
|
|
132
|
-
<span className="text-[8px] text-slate-500">{d.confidence}% conf</span>
|
|
133
|
-
</div>
|
|
134
|
-
</div>
|
|
135
|
-
))}
|
|
136
|
-
</>
|
|
137
|
-
)}
|
|
138
|
-
|
|
139
|
-
{/* Churn Prevention */}
|
|
140
|
-
{tab === 'churn' && (
|
|
141
|
-
<>
|
|
142
|
-
<div className="text-[9px] text-slate-500 font-bold uppercase flex items-center gap-1 mb-1">
|
|
143
|
-
<AlertTriangle size={10} className="text-red-400" /> Clientes en Riesgo de Pérdida
|
|
144
|
-
</div>
|
|
145
|
-
{CHURN_RISKS.map((c, i) => (
|
|
146
|
-
<div key={i} className="bg-slate-900/50 rounded-lg p-2.5 border border-slate-800"
|
|
147
|
-
style={{ borderLeftWidth: '2px', borderLeftColor: c.risk > 70 ? '#ef4444' : c.risk > 50 ? '#f59e0b' : '#22c55e' }}>
|
|
148
|
-
<div className="flex items-center justify-between mb-1">
|
|
149
|
-
<span className="text-[11px] font-bold">{c.name}</span>
|
|
150
|
-
<span className={`text-sm font-black ${c.risk > 70 ? 'text-red-400' : c.risk > 50 ? 'text-yellow-400' : 'text-green-400'}`}>
|
|
151
|
-
{c.risk}%
|
|
152
|
-
</span>
|
|
153
|
-
</div>
|
|
154
|
-
<div className="text-[9px] text-slate-500 mb-1">{c.model}</div>
|
|
155
|
-
<div className="text-[9px] text-slate-400 mb-1">📋 {c.reason}</div>
|
|
156
|
-
<div className="text-[9px] text-purple-400 font-semibold flex items-center gap-1">
|
|
157
|
-
<Target size={8} /> {c.action}
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
))}
|
|
161
|
-
</>
|
|
162
|
-
)}
|
|
163
|
-
|
|
164
|
-
{/* Lead Conversion */}
|
|
165
|
-
{tab === 'leads' && (
|
|
166
|
-
<>
|
|
167
|
-
<div className="text-[9px] text-slate-500 font-bold uppercase flex items-center gap-1 mb-1">
|
|
168
|
-
<TrendingUp size={10} className="text-blue-400" /> Predicción de Conversión por Canal
|
|
169
|
-
</div>
|
|
170
|
-
{LEAD_CONVERSION.map(l => (
|
|
171
|
-
<div key={l.source} className="bg-slate-900/50 rounded-lg p-2.5 border border-slate-800">
|
|
172
|
-
<div className="flex items-center justify-between mb-1">
|
|
173
|
-
<span className="text-[11px] font-bold">{l.source}</span>
|
|
174
|
-
<div className="text-right">
|
|
175
|
-
<span className="text-sm font-bold text-green-400">{l.predicted}</span>
|
|
176
|
-
<span className="text-[9px] text-slate-500"> / {l.leads}</span>
|
|
177
|
-
</div>
|
|
178
|
-
</div>
|
|
179
|
-
<div className="flex items-center gap-2">
|
|
180
|
-
<div className="flex-1 h-1.5 bg-slate-800 rounded-full">
|
|
181
|
-
<div className="h-full rounded-full bg-linear-to-r from-green-500 to-emerald-400"
|
|
182
|
-
style={{ width: `${Math.min(100, l.rate * 2.5)}%` }} />
|
|
183
|
-
</div>
|
|
184
|
-
<span className="text-[9px] font-bold text-green-400">{l.rate}%</span>
|
|
185
|
-
<span className={`text-[8px] ${l.trend > 0 ? 'text-green-400' : 'text-red-400'}`}>
|
|
186
|
-
{l.trend > 0 ? '↑' : '↓'}{Math.abs(l.trend)}%
|
|
187
|
-
</span>
|
|
188
|
-
</div>
|
|
189
|
-
</div>
|
|
190
|
-
))}
|
|
191
|
-
</>
|
|
192
|
-
)}
|
|
193
|
-
|
|
194
|
-
{/* Parts Demand */}
|
|
195
|
-
{tab === 'parts' && (
|
|
196
|
-
<>
|
|
197
|
-
<div className="text-[9px] text-slate-500 font-bold uppercase flex items-center gap-1 mb-1">
|
|
198
|
-
<ShoppingCart size={10} className="text-cyan-400" /> Predicción Demanda Repuestos
|
|
199
|
-
</div>
|
|
200
|
-
{PARTS_FORECAST.map(p => (
|
|
201
|
-
<div key={p.part} className={`bg-slate-900/50 rounded-lg p-2.5 border ${p.reorder ? 'border-red-500/30' : 'border-slate-800'
|
|
202
|
-
}`}>
|
|
203
|
-
<div className="flex items-center justify-between mb-1">
|
|
204
|
-
<span className="text-[10px] font-bold">{p.part}</span>
|
|
205
|
-
{p.reorder && (
|
|
206
|
-
<span className="text-[8px] px-1.5 py-0.5 bg-red-500/20 text-red-400 rounded font-bold animate-pulse">
|
|
207
|
-
⚠️ REORDER
|
|
208
|
-
</span>
|
|
209
|
-
)}
|
|
210
|
-
</div>
|
|
211
|
-
<div className="flex items-center gap-3 text-[9px]">
|
|
212
|
-
<span className="text-slate-400">Stock: <span className="font-bold text-white">{p.current}</span></span>
|
|
213
|
-
<span className="text-slate-400">Demand: <span className="font-bold text-yellow-400">{p.predicted}/mes</span></span>
|
|
214
|
-
<span className={`font-bold ${p.daysToStockout < 7 ? 'text-red-400' : p.daysToStockout < 14 ? 'text-yellow-400' : 'text-green-400'}`}>
|
|
215
|
-
{p.daysToStockout}d stock
|
|
216
|
-
</span>
|
|
217
|
-
</div>
|
|
218
|
-
</div>
|
|
219
|
-
))}
|
|
220
|
-
</>
|
|
221
|
-
)}
|
|
222
|
-
</div>
|
|
223
|
-
</div>
|
|
224
|
-
</BaseNode>
|
|
225
|
-
);
|
|
226
|
-
};
|