@decido/ui 0.0.0
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/.eslintrc.cjs +10 -0
- package/.turbo/turbo-build.log +13 -0
- package/.turbo/turbo-lint.log +54 -0
- package/dist/components/DebugPanel.d.ts +3 -0
- package/dist/components/DebugPanel.js +195 -0
- package/dist/components/DebugPanelManager.d.ts +3 -0
- package/dist/components/DebugPanelManager.js +20 -0
- package/dist/components/DecidoButton.d.ts +11 -0
- package/dist/components/DecidoButton.js +15 -0
- package/dist/components/LoginView.d.ts +6 -0
- package/dist/components/LoginView.js +36 -0
- package/dist/components/WelcomeView.d.ts +6 -0
- package/dist/components/WelcomeView.js +16 -0
- package/dist/components/base/BaseComponent.d.ts +12 -0
- package/dist/components/base/BaseComponent.js +43 -0
- package/dist/components/base/BaseComponentBacjup.d.ts +12 -0
- package/dist/components/base/BaseComponentBacjup.js +43 -0
- package/dist/components/base/BaseComponentLit.d.ts +9 -0
- package/dist/components/base/BaseComponentLit.js +30 -0
- package/dist/components/base/shared-styles.d.ts +4 -0
- package/dist/components/base/shared-styles.js +18 -0
- package/dist/components/index.d.ts +6 -0
- package/dist/components/index.js +7 -0
- package/dist/components/landing/index.d.ts +11 -0
- package/dist/components/landing/index.js +11 -0
- package/dist/components/landing/landing-button.d.ts +9 -0
- package/dist/components/landing/landing-button.js +5 -0
- package/dist/components/landing/landing-card.d.ts +6 -0
- package/dist/components/landing/landing-card.js +5 -0
- package/dist/components/landing/landing-cta.d.ts +10 -0
- package/dist/components/landing/landing-cta.js +5 -0
- package/dist/components/landing/landing-feature-card.d.ts +35 -0
- package/dist/components/landing/landing-feature-card.js +35 -0
- package/dist/components/landing/landing-footer.d.ts +18 -0
- package/dist/components/landing/landing-footer.js +7 -0
- package/dist/components/landing/landing-header.d.ts +21 -0
- package/dist/components/landing/landing-header.js +7 -0
- package/dist/components/landing/landing-hero.d.ts +22 -0
- package/dist/components/landing/landing-hero.js +10 -0
- package/dist/components/landing/landing-page.d.ts +3 -0
- package/dist/components/landing/landing-page.js +34 -0
- package/dist/components/landing/landing-section.d.ts +14 -0
- package/dist/components/landing/landing-section.js +8 -0
- package/dist/components/landing/landing-testimonial-card.d.ts +11 -0
- package/dist/components/landing/landing-testimonial-card.js +9 -0
- package/dist/components/landing/landing-waitlist.d.ts +27 -0
- package/dist/components/landing/landing-waitlist.js +18 -0
- package/dist/components/theming/ColorPicker.d.ts +8 -0
- package/dist/components/theming/ColorPicker.js +19 -0
- package/dist/components/theming/ThemePanel.d.ts +5 -0
- package/dist/components/theming/ThemePanel.js +12 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +19 -0
- package/dist/utils/counter.d.ts +1 -0
- package/dist/utils/counter.js +9 -0
- package/eslint.config.js +11 -0
- package/package.json +47 -0
- package/src/components/DebugPanel.tsx +347 -0
- package/src/components/DebugPanelManager.tsx +40 -0
- package/src/components/DecidoButton.tsx +43 -0
- package/src/components/LoginView.tsx +85 -0
- package/src/components/WelcomeView.tsx +47 -0
- package/src/components/base/landing.css +477 -0
- package/src/components/base/shared-styles.ts +21 -0
- package/src/components/index.ts +7 -0
- package/src/components/landing/index.tsx +11 -0
- package/src/components/landing/landing-button.tsx +26 -0
- package/src/components/landing/landing-card.tsx +15 -0
- package/src/components/landing/landing-cta.tsx +42 -0
- package/src/components/landing/landing-feature-card.tsx +149 -0
- package/src/components/landing/landing-footer.tsx +66 -0
- package/src/components/landing/landing-header.tsx +55 -0
- package/src/components/landing/landing-hero.tsx +94 -0
- package/src/components/landing/landing-page.tsx +143 -0
- package/src/components/landing/landing-section.tsx +42 -0
- package/src/components/landing/landing-testimonial-card.tsx +50 -0
- package/src/components/landing/landing-waitlist.tsx +115 -0
- package/src/components/theming/ColorPicker.tsx +40 -0
- package/src/components/theming/ThemePanel.tsx +43 -0
- package/src/css/base.css +483 -0
- package/src/css/components.css +309 -0
- package/src/css/debug-panel.css +1368 -0
- package/src/css/layout.css +289 -0
- package/src/css/login-view.css +45 -0
- package/src/css/style.css +20 -0
- package/src/css/welcome-view.css +56 -0
- package/src/css/workbench.css +383 -0
- package/src/globals.d.ts +8 -0
- package/src/index.ts +24 -0
- package/src/utils/counter.d.ts.map +1 -0
- package/src/utils/counter.js +9 -0
- package/src/utils/counter.ts +9 -0
- package/src/vite-env.d.ts +1 -0
- package/tailwind.config.js +7 -0
- package/tsconfig.json +35 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
|
2
|
+
import { ConsoleLog } from '@decido/shell-vscode-core';
|
|
3
|
+
|
|
4
|
+
// --- Tipos para el grafo de arquitectura ---
|
|
5
|
+
interface GraphNode {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
status: 'idle' | 'loaded' | 'active' | 'error';
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
fx: number | null; // Posición X fija (para arrastrar)
|
|
14
|
+
fy: number | null; // Posición Y fija (para arrastrar)
|
|
15
|
+
metadata?: any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface GraphLink {
|
|
19
|
+
source: string | GraphNode;
|
|
20
|
+
target: string | GraphNode;
|
|
21
|
+
label: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Un serializador JSON seguro que maneja referencias circulares, funciones y elementos del DOM.
|
|
26
|
+
* @param value El objeto a serializar.
|
|
27
|
+
* @returns Una cadena de texto JSON segura.
|
|
28
|
+
*/
|
|
29
|
+
function safeJsonStringify(value: any): string {
|
|
30
|
+
const cache = new Set();
|
|
31
|
+
const replacer = (_key: string, value: any) => {
|
|
32
|
+
if (typeof value === 'object' && value !== null) {
|
|
33
|
+
if (cache.has(value)) {
|
|
34
|
+
// Referencia circular detectada
|
|
35
|
+
return '[Circular Reference]';
|
|
36
|
+
}
|
|
37
|
+
cache.add(value);
|
|
38
|
+
}
|
|
39
|
+
if (typeof value === 'function') {
|
|
40
|
+
return `[Function: ${value.name || 'anonymous'}]`;
|
|
41
|
+
}
|
|
42
|
+
if (value instanceof HTMLElement) {
|
|
43
|
+
return `[HTMLElement: ${value.tagName}${value.id ? '#' + value.id : ''}]`;
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Usamos el 'replacer' y una indentación de 2 espacios para que sea legible.
|
|
49
|
+
return JSON.stringify(value, replacer, 2);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const DebugPanel: React.FC = () => {
|
|
53
|
+
const debugService = (window as any).decidoAPI?.debug; // Asumo que el debugService está en window.decidoAPI.debug
|
|
54
|
+
const [nodes, setNodes] = useState<GraphNode[]>([]);
|
|
55
|
+
const [links, setLinks] = useState<GraphLink[]>([]);
|
|
56
|
+
const [hoveredNode, setHoveredNode] = useState<GraphNode | null>(null);
|
|
57
|
+
const [activeTab, setActiveTab] = useState<'graph' | 'console'>('graph');
|
|
58
|
+
const panelRef = useRef<HTMLDivElement>(null);
|
|
59
|
+
const [panelPosition, setPanelPosition] = useState({ bottom: 20, right: 20, left: 'auto', top: 'auto' });
|
|
60
|
+
|
|
61
|
+
const width = 800;
|
|
62
|
+
const height = 500;
|
|
63
|
+
|
|
64
|
+
// --- Lógica de Arrastre del Panel ---
|
|
65
|
+
const isPanelDragging = useRef(false);
|
|
66
|
+
const panelOffset = useRef({ x: 0, y: 0 });
|
|
67
|
+
|
|
68
|
+
const onPanelDragStart = useCallback((e: React.MouseEvent) => {
|
|
69
|
+
if (e.button !== 0) return; // Solo arrastrar con el botón izquierdo del ratón
|
|
70
|
+
isPanelDragging.current = true;
|
|
71
|
+
const panelRect = panelRef.current!.getBoundingClientRect();
|
|
72
|
+
panelOffset.current = {
|
|
73
|
+
x: e.clientX - panelRect.left,
|
|
74
|
+
y: e.clientY - panelRect.top,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
document.addEventListener('mousemove', onPanelDrag);
|
|
78
|
+
document.addEventListener('mouseup', onPanelDragEnd);
|
|
79
|
+
document.body.style.userSelect = 'none'; // Evitar selección de texto durante el arrastre
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
const onPanelDrag = useCallback((e: MouseEvent) => {
|
|
83
|
+
if (!isPanelDragging.current) return;
|
|
84
|
+
const x = e.clientX - panelOffset.current.x;
|
|
85
|
+
const y = e.clientY - panelOffset.current.y;
|
|
86
|
+
setPanelPosition({ bottom: 'auto', right: 'auto', left: x, top: y } as any);
|
|
87
|
+
}, []);
|
|
88
|
+
|
|
89
|
+
const onPanelDragEnd = useCallback(() => {
|
|
90
|
+
isPanelDragging.current = false;
|
|
91
|
+
document.removeEventListener('mousemove', onPanelDrag);
|
|
92
|
+
document.removeEventListener('mouseup', onPanelDragEnd);
|
|
93
|
+
document.body.style.userSelect = ''; // Restaurar la selección de texto
|
|
94
|
+
}, []);
|
|
95
|
+
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (!debugService) return;
|
|
98
|
+
|
|
99
|
+
// Mock subscription for effect - Assuming debugService triggers reloads
|
|
100
|
+
const graph = debugService.graphState[0]();
|
|
101
|
+
setNodes(prevNodes => {
|
|
102
|
+
if (graph.nodes.length > 0 && prevNodes.length === 0) {
|
|
103
|
+
return initializeNodePositions(graph.nodes);
|
|
104
|
+
}
|
|
105
|
+
return graph.nodes;
|
|
106
|
+
});
|
|
107
|
+
setLinks(graph.links);
|
|
108
|
+
|
|
109
|
+
return () => {
|
|
110
|
+
// Unsubscribe mock
|
|
111
|
+
};
|
|
112
|
+
}, [debugService]);
|
|
113
|
+
|
|
114
|
+
const initializeNodePositions = useCallback((newNodes: GraphNode[]): GraphNode[] => {
|
|
115
|
+
const savedPositions = loadNodePositions();
|
|
116
|
+
return newNodes.map(node => {
|
|
117
|
+
if (savedPositions[node.id]) {
|
|
118
|
+
return { ...node, x: savedPositions[node.id].x, y: savedPositions[node.id].y };
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
...node,
|
|
122
|
+
x: node.x === undefined ? Math.random() * (width - 100) + 50 : node.x,
|
|
123
|
+
y: node.y === undefined ? Math.random() * (height - 100) + 50 : node.y,
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
}, [width, height]);
|
|
127
|
+
|
|
128
|
+
const saveNodePositions = useCallback((currentNodes: GraphNode[]) => {
|
|
129
|
+
const positions = currentNodes.reduce((acc, node) => {
|
|
130
|
+
acc[node.id] = { x: node.x, y: node.y };
|
|
131
|
+
return acc;
|
|
132
|
+
}, {} as Record<string, { x: number, y: number }>);
|
|
133
|
+
localStorage.setItem('decido_debug_panel_node_positions', JSON.stringify(positions));
|
|
134
|
+
}, []);
|
|
135
|
+
|
|
136
|
+
const loadNodePositions = (): Record<string, { x: number, y: number }> => {
|
|
137
|
+
try {
|
|
138
|
+
const saved = localStorage.getItem('decido_debug_panel_node_positions');
|
|
139
|
+
return saved ? JSON.parse(saved) : {};
|
|
140
|
+
} catch (e) {
|
|
141
|
+
console.error("Error cargando posiciones de nodos:", e);
|
|
142
|
+
return {};
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// --- Lógica de Arrastre de Nodos ---
|
|
147
|
+
const draggedNode = useRef<GraphNode | null>(null);
|
|
148
|
+
const nodeOffset = useRef({ x: 0, y: 0 });
|
|
149
|
+
|
|
150
|
+
const onNodeDragStart = useCallback((e: React.MouseEvent, node: GraphNode) => {
|
|
151
|
+
e.stopPropagation();
|
|
152
|
+
draggedNode.current = node;
|
|
153
|
+
nodeOffset.current = {
|
|
154
|
+
x: e.clientX - node.x,
|
|
155
|
+
y: e.clientY - node.y,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
setNodes(prevNodes => prevNodes.map(n => n.id === node.id ? { ...n, fx: node.x, fy: node.y } : n));
|
|
159
|
+
|
|
160
|
+
document.addEventListener('mousemove', onNodeDrag);
|
|
161
|
+
document.addEventListener('mouseup', onNodeDragEnd);
|
|
162
|
+
document.body.style.userSelect = 'none';
|
|
163
|
+
}, [nodes]);
|
|
164
|
+
|
|
165
|
+
const onNodeDrag = useCallback((e: MouseEvent) => {
|
|
166
|
+
if (!draggedNode.current) return;
|
|
167
|
+
const newFx = e.clientX - nodeOffset.current.x;
|
|
168
|
+
const newFy = e.clientY - nodeOffset.current.y;
|
|
169
|
+
setNodes(prevNodes => prevNodes.map(n => n.id === draggedNode.current?.id ? { ...n, fx: newFx, fy: newFy } : n));
|
|
170
|
+
}, []);
|
|
171
|
+
|
|
172
|
+
const onNodeDragEnd = useCallback(() => {
|
|
173
|
+
if (draggedNode.current) {
|
|
174
|
+
setNodes(prevNodes => {
|
|
175
|
+
const updatedNodes = prevNodes.map(n => {
|
|
176
|
+
if (n.id === draggedNode.current?.id) {
|
|
177
|
+
return { ...n, x: n.fx!, y: n.fy!, fx: null, fy: null };
|
|
178
|
+
}
|
|
179
|
+
return n;
|
|
180
|
+
});
|
|
181
|
+
saveNodePositions(updatedNodes);
|
|
182
|
+
return updatedNodes;
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
draggedNode.current = null;
|
|
186
|
+
document.removeEventListener('mousemove', onNodeDrag);
|
|
187
|
+
document.removeEventListener('mouseup', onNodeDragEnd);
|
|
188
|
+
document.body.style.userSelect = '';
|
|
189
|
+
}, [saveNodePositions]);
|
|
190
|
+
|
|
191
|
+
const onMouseOverNode = useCallback((node: GraphNode) => { setHoveredNode(node); }, []);
|
|
192
|
+
const onMouseOutNode = useCallback(() => { setHoveredNode(null); }, []);
|
|
193
|
+
|
|
194
|
+
// --- Renderizado de Logs de Consola ---
|
|
195
|
+
const renderConsoleLog = (log: ConsoleLog) => {
|
|
196
|
+
const formattedMessage = log.message.map(m => {
|
|
197
|
+
if (typeof m === 'object' && m !== null) {
|
|
198
|
+
return safeJsonStringify(m);
|
|
199
|
+
}
|
|
200
|
+
return String(m);
|
|
201
|
+
}).join(' ');
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<div key={log.id} className={`log-entry log-level-${log.type}`}>
|
|
205
|
+
<span className="log-timestamp">{new Date(log.timestamp).toLocaleTimeString()}</span>
|
|
206
|
+
<pre className="log-message">{formattedMessage}</pre>
|
|
207
|
+
</div>
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const handleMapChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
|
212
|
+
const path = e.target.value;
|
|
213
|
+
debugService?.loadArchitectureGraph(path);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const log = debugService?.probeLog[0]();
|
|
217
|
+
const playback = debugService?.playbackState[0]();
|
|
218
|
+
const consoleLogs = debugService?.consoleLogs[0]();
|
|
219
|
+
|
|
220
|
+
const renderGraphContent = (currentNodes: GraphNode[], currentLinks: GraphLink[]) => {
|
|
221
|
+
const populatedLinks = currentLinks
|
|
222
|
+
.map(link => ({
|
|
223
|
+
...link,
|
|
224
|
+
source: currentNodes.find(n => n.id === link.source)!,
|
|
225
|
+
target: currentNodes.find(n => n.id === link.target)!
|
|
226
|
+
}))
|
|
227
|
+
.filter(l => l.source && l.target);
|
|
228
|
+
|
|
229
|
+
return (
|
|
230
|
+
<svg id="architecture-svg" width={width} height={height - 40}>
|
|
231
|
+
<g className="links">
|
|
232
|
+
{populatedLinks.map((link, index) => (
|
|
233
|
+
<line
|
|
234
|
+
key={index}
|
|
235
|
+
x1={link.source.x} y1={link.source.y}
|
|
236
|
+
x2={link.target.x} y2={link.target.y}
|
|
237
|
+
stroke="#475569" strokeWidth="1.5"
|
|
238
|
+
/>
|
|
239
|
+
))}
|
|
240
|
+
</g>
|
|
241
|
+
<g className="nodes">
|
|
242
|
+
{currentNodes.map(node => (
|
|
243
|
+
<g
|
|
244
|
+
key={node.id}
|
|
245
|
+
className="node-group"
|
|
246
|
+
transform={`translate(${node.fx ?? node.x}, ${node.fy ?? node.y})`}
|
|
247
|
+
onMouseDown={(e) => onNodeDragStart(e, node)}
|
|
248
|
+
onMouseOver={() => onMouseOverNode(node)}
|
|
249
|
+
onMouseOut={onMouseOutNode}
|
|
250
|
+
>
|
|
251
|
+
<circle r="12" className={`node-circle type-${node.type} status-${node.status}`} />
|
|
252
|
+
<text x="16" y="4" className="node-label">{node.label}</text>
|
|
253
|
+
</g>
|
|
254
|
+
))}
|
|
255
|
+
</g>
|
|
256
|
+
{hoveredNode && renderTooltip(hoveredNode)}
|
|
257
|
+
</svg>
|
|
258
|
+
);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const renderTooltip = (node: GraphNode) => {
|
|
262
|
+
return (
|
|
263
|
+
<foreignObject x={(node.x || 0) + 20} y={(node.y || 0) + 20} width="250" height="150">
|
|
264
|
+
<div className="tooltip-container">
|
|
265
|
+
<div className={`tooltip-header type-${node.type}`}>{node.label}</div>
|
|
266
|
+
<div className="tooltip-body">
|
|
267
|
+
<p><strong>ID:</strong> {node.id}</p>
|
|
268
|
+
<p><strong>Tipo:</strong> {node.type}</p>
|
|
269
|
+
<p><strong>Estado:</strong> <span className={`status-${node.status}`}>{node.status}</span></p>
|
|
270
|
+
<p className="tooltip-desc">{node.description || 'Sin descripción.'}</p>
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
</foreignObject>
|
|
274
|
+
);
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
return (
|
|
278
|
+
<div
|
|
279
|
+
ref={panelRef}
|
|
280
|
+
className="debug-panel"
|
|
281
|
+
style={{
|
|
282
|
+
bottom: panelPosition.bottom,
|
|
283
|
+
right: panelPosition.right,
|
|
284
|
+
left: panelPosition.left,
|
|
285
|
+
top: panelPosition.top,
|
|
286
|
+
width: width,
|
|
287
|
+
height: height,
|
|
288
|
+
}}
|
|
289
|
+
>
|
|
290
|
+
<div className="debug-header" onMouseDown={onPanelDragStart}>
|
|
291
|
+
<span><i className="fas fa-microchip mr-2"></i>Escáner de Arquitectura</span>
|
|
292
|
+
<select onChange={handleMapChange} style={{ background: '#334155', border: 'none', color: 'white', borderRadius: '4px', fontSize: '11px' }}>
|
|
293
|
+
<option value="/architecture-map.json">Vista General</option>
|
|
294
|
+
<option value="/maps/auth-flow.json">Flujo de Autenticación</option>
|
|
295
|
+
<option value="/maps/kanban-flow.json">Flujo de Kanban</option>
|
|
296
|
+
</select>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
{/* Selector de Pestañas */}
|
|
300
|
+
<div className="debug-tabs">
|
|
301
|
+
<button className={`debug-tab ${activeTab === 'graph' ? 'active' : ''}`} onClick={() => setActiveTab('graph')}>
|
|
302
|
+
<i className="fas fa-sitemap mr-2"></i>Arquitectura
|
|
303
|
+
</button>
|
|
304
|
+
<button className={`debug-tab ${activeTab === 'console' ? 'active' : ''}`} onClick={() => setActiveTab('console')}>
|
|
305
|
+
<i className="fas fa-terminal mr-2"></i>Consola
|
|
306
|
+
</button>
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
<div className="debug-content">
|
|
310
|
+
{/* Contenido del Grafo (condicional) */}
|
|
311
|
+
{activeTab === 'graph' && (
|
|
312
|
+
<div className="tab-content active">
|
|
313
|
+
{renderGraphContent(nodes, links)}
|
|
314
|
+
</div>
|
|
315
|
+
)}
|
|
316
|
+
{/* Contenido de la Consola (condicional) */}
|
|
317
|
+
{activeTab === 'console' && (
|
|
318
|
+
<div className="tab-content active">
|
|
319
|
+
<div className="console-log-list">
|
|
320
|
+
{consoleLogs?.map((log: any) => renderConsoleLog(log))}
|
|
321
|
+
</div>
|
|
322
|
+
</div>
|
|
323
|
+
)}
|
|
324
|
+
</div>
|
|
325
|
+
{/* NUEVA BARRA DE CONTROL */}
|
|
326
|
+
<div className="debug-footer">
|
|
327
|
+
<div className="playback-controls flex gap-2">
|
|
328
|
+
<button onClick={() => debugService?.goToLogIndex(0)} title="Ir al Inicio"><i className="fas fa-fast-backward"></i></button>
|
|
329
|
+
<button onClick={() => debugService?.goToLogIndex(playback?.currentIndex - 1)} title="Paso Anterior"><i className="fas fa-step-backward"></i></button>
|
|
330
|
+
<button onClick={() => { /* Lógica de Play/Pause */ }} title="Reproducir"><i className="fas fa-play"></i></button>
|
|
331
|
+
<button onClick={() => debugService?.goToLogIndex(playback?.currentIndex + 1)} title="Paso Siguiente"><i className="fas fa-step-forward"></i></button>
|
|
332
|
+
</div>
|
|
333
|
+
<input
|
|
334
|
+
type="range"
|
|
335
|
+
className="timeline-slider"
|
|
336
|
+
min="0"
|
|
337
|
+
max={log?.length > 0 ? log.length - 1 : 0}
|
|
338
|
+
value={playback?.currentIndex || 0}
|
|
339
|
+
onChange={(e) => debugService?.goToLogIndex(parseInt(e.target.value))}
|
|
340
|
+
/>
|
|
341
|
+
<span className="text-xs text-muted-foreground w-20 text-center">{playback?.currentIndex + 1} / {log?.length}</span>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
);
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
export default DebugPanel;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import DebugPanel from './DebugPanel'; // Import the React component
|
|
3
|
+
|
|
4
|
+
// Assuming DebugPanelManager is intended to render DebugPanel based on some service state.
|
|
5
|
+
// We'll mimic the previous logic, assuming `this.api.debug.panels` holds the state.
|
|
6
|
+
// This example assumes `panels` is a signal from `@decido/core` (Zustand-like state).
|
|
7
|
+
|
|
8
|
+
interface PanelState {
|
|
9
|
+
id: string;
|
|
10
|
+
isVisible: boolean;
|
|
11
|
+
// Add other properties that your panel might have
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const DebugPanelManager: React.FC = () => {
|
|
15
|
+
const [panels, setPanels] = useState<PanelState[]>([]);
|
|
16
|
+
const panelService = (window as any).decidoAPI?.debug?.panels; // Access the service from window.decidoAPI
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!panelService) return;
|
|
20
|
+
|
|
21
|
+
// Assuming panelService.panels is a signal, we'd subscribe to it
|
|
22
|
+
const unsubscribe = () => { }; // MOCK: createEffect was from missing import
|
|
23
|
+
const currentPanels = Object.values(panelService.panels[0]());
|
|
24
|
+
setPanels(currentPanels as PanelState[]);
|
|
25
|
+
|
|
26
|
+
return () => {
|
|
27
|
+
unsubscribe();
|
|
28
|
+
};
|
|
29
|
+
}, [panelService]);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
{panels.filter(p => p.isVisible).map(p => (
|
|
34
|
+
<DebugPanel key={p.id} /* pass props as needed based on PanelState */ />
|
|
35
|
+
))}
|
|
36
|
+
</>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default DebugPanelManager;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface DecidoButtonProps {
|
|
4
|
+
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
5
|
+
type?: 'button' | 'submit' | 'reset';
|
|
6
|
+
variant?: 'primary' | 'secondary';
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
className?: string;
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const DecidoButton: React.FC<DecidoButtonProps> = ({
|
|
13
|
+
onClick,
|
|
14
|
+
type = 'button',
|
|
15
|
+
variant = 'primary',
|
|
16
|
+
disabled = false,
|
|
17
|
+
className = '',
|
|
18
|
+
children,
|
|
19
|
+
}) => {
|
|
20
|
+
const baseClasses = `btn btn-${variant} ${className}`.trim();
|
|
21
|
+
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
22
|
+
if (type === 'submit') {
|
|
23
|
+
const form = (event.currentTarget as HTMLButtonElement).closest('form');
|
|
24
|
+
if (form) {
|
|
25
|
+
form.dispatchEvent(new Event('submit', { cancelable: true, bubbles: true }));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
onClick?.(event);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<button
|
|
33
|
+
type={type}
|
|
34
|
+
className={baseClasses}
|
|
35
|
+
disabled={disabled}
|
|
36
|
+
onClick={handleClick}
|
|
37
|
+
>
|
|
38
|
+
{children}
|
|
39
|
+
</button>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default DecidoButton;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import '../css/login-view.css';
|
|
3
|
+
|
|
4
|
+
interface LoginViewProps {
|
|
5
|
+
// Define props if any, for now, it takes none.
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const LoginView: React.FC<LoginViewProps> = () => {
|
|
9
|
+
const [email, setEmail] = useState('ing_julioramirez@hotmail.com');
|
|
10
|
+
const [password, setPassword] = useState('y0s0ft..');
|
|
11
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
12
|
+
const api = (window as any).decidoAPI;
|
|
13
|
+
|
|
14
|
+
const handleLogin = async (e: React.FormEvent) => {
|
|
15
|
+
e.preventDefault();
|
|
16
|
+
setIsLoading(true);
|
|
17
|
+
api.debug?.probe('ui:LoginView', 'active');
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
await api.auth.login(email, password);
|
|
21
|
+
// On success, the router and auth service should handle the redirect.
|
|
22
|
+
// No need to manually navigate here.
|
|
23
|
+
} catch (error: any) {
|
|
24
|
+
const errorMessage = error instanceof Error ? error.message : "Error desconocido";
|
|
25
|
+
api.notifications.show('error', 'Error de Login', errorMessage);
|
|
26
|
+
} finally {
|
|
27
|
+
setIsLoading(false);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const handleRegisterClick = () => {
|
|
32
|
+
if (api && api.commands) {
|
|
33
|
+
api.commands.executeCommand("auth.showRegisterModal");
|
|
34
|
+
} else {
|
|
35
|
+
console.warn('Decido API o Command Service no disponible.');
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div className="login-container animate-fade-in">
|
|
41
|
+
|
|
42
|
+
<div className="login-box">
|
|
43
|
+
<h1 className="login-title">Iniciar Sesión</h1>
|
|
44
|
+
<form className="login-form" onSubmit={handleLogin}>
|
|
45
|
+
<div className="form-group">
|
|
46
|
+
<label htmlFor="email" className="journal-label">Email</label>
|
|
47
|
+
<input
|
|
48
|
+
type="email"
|
|
49
|
+
id="email"
|
|
50
|
+
name="email"
|
|
51
|
+
className="form-input-premium"
|
|
52
|
+
required
|
|
53
|
+
value={email}
|
|
54
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
55
|
+
disabled={isLoading}
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
<div className="form-group">
|
|
59
|
+
<label htmlFor="password" className="journal-label">Contraseña</label>
|
|
60
|
+
<input
|
|
61
|
+
type="password"
|
|
62
|
+
id="password"
|
|
63
|
+
name="password"
|
|
64
|
+
className="form-input-premium"
|
|
65
|
+
required
|
|
66
|
+
value={password}
|
|
67
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
68
|
+
disabled={isLoading}
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
<div className="form-actions">
|
|
72
|
+
<button type="submit" className="btn-premium w-full" disabled={isLoading}>
|
|
73
|
+
{isLoading ? <i className="fas fa-spinner fa-spin"></i> : ''} Entrar
|
|
74
|
+
</button>
|
|
75
|
+
</div>
|
|
76
|
+
</form>
|
|
77
|
+
<div className="register-link">
|
|
78
|
+
<a href="#" onClick={handleRegisterClick}>¿No tienes cuenta? Regístrate</a>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export default LoginView;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import '../css/welcome-view.css';
|
|
3
|
+
|
|
4
|
+
interface WelcomeViewProps {
|
|
5
|
+
// Define props if any, for now, it takes none.
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const WelcomeView: React.FC<WelcomeViewProps> = () => {
|
|
9
|
+
// Access the API from the window object
|
|
10
|
+
const api = (window as any).decidoAPI;
|
|
11
|
+
|
|
12
|
+
const handleQuickLinkClick = (commandId: string, commandArgs?: any) => {
|
|
13
|
+
if (api && api.commands) {
|
|
14
|
+
api.commands.executeCommand(commandId, commandArgs);
|
|
15
|
+
} else {
|
|
16
|
+
console.warn('Decido API o Command Service no disponible.');
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="welcome-container animate-fade-in">
|
|
22
|
+
|
|
23
|
+
<div className="logo">
|
|
24
|
+
<i className="fas fa-brain"></i>
|
|
25
|
+
</div>
|
|
26
|
+
<h1 className="welcome-title">Bienvenido a Decido</h1>
|
|
27
|
+
<p className="welcome-subtitle">Tu sistema operativo para la vida. ¿Qué quieres lograr hoy?</p>
|
|
28
|
+
|
|
29
|
+
<div className="quick-links-grid">
|
|
30
|
+
<a href="#" className="quick-link" onClick={() => handleQuickLinkClick("workbench.openTab", { id: "dashboard", title: "Dashboard", componentTag: "dashboard-view", icon: "fas fa-home" })}>
|
|
31
|
+
<i className="fas fa-home"></i>
|
|
32
|
+
<span>Ver mi Dashboard</span>
|
|
33
|
+
</a>
|
|
34
|
+
<a href="#" className="quick-link" onClick={() => handleQuickLinkClick("workbench.openTab", { id: "goal-architect", title: "Arquitecto de Metas", componentTag: "goal-architect-view", icon: "fas fa-compass" })}>
|
|
35
|
+
<i className="fas fa-compass"></i>
|
|
36
|
+
<span>Definir una Meta</span>
|
|
37
|
+
</a>
|
|
38
|
+
<a href="#" className="quick-link" onClick={() => handleQuickLinkClick("palette.toggle")}>
|
|
39
|
+
<i className="fas fa-terminal"></i>
|
|
40
|
+
<span>Abrir Paleta de Comandos</span>
|
|
41
|
+
</a>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default WelcomeView;
|