roadmap-kit 1.0.1 → 1.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/cli.js +611 -26
- package/dashboard/index.html +7 -5
- package/dashboard/server.js +35 -10
- package/dashboard/src/App.jsx +571 -175
- package/dashboard/src/index.css +555 -304
- package/dashboard/src/main.jsx +2 -2
- package/dashboard/src/themes.js +50 -0
- package/dashboard/tailwind.config.js +7 -1
- package/dashboard/vite.config.js +3 -0
- package/package.json +1 -1
- package/dashboard/dist/assets/index-BzYzLB7u.css +0 -1
- package/dashboard/dist/assets/index-DIonhzlK.js +0 -506
- package/dashboard/dist/index.html +0 -18
- package/dashboard/dist/roadmap.json +0 -268
package/dashboard/src/App.jsx
CHANGED
|
@@ -8,8 +8,10 @@ import {
|
|
|
8
8
|
HelpCircle, GitCommit, Bot, Wrench, Activity, Grid, Play,
|
|
9
9
|
ArrowRight, Minus as MinusIcon, PlusCircle, MinusCircle, GitBranch,
|
|
10
10
|
Globe, Command, Columns, List, Link2, MessageSquare, Timer,
|
|
11
|
-
BarChart3, FileDown, History, Bell, Keyboard, Filter
|
|
11
|
+
BarChart3, FileDown, History, Bell, Keyboard, Filter, Palette,
|
|
12
|
+
Sun, Moon
|
|
12
13
|
} from 'lucide-react';
|
|
14
|
+
import { themes, DEFAULT_THEME, getTheme } from './themes.js';
|
|
13
15
|
|
|
14
16
|
// ============ TOAST NOTIFICATION SYSTEM ============
|
|
15
17
|
const ToastContext = createContext();
|
|
@@ -75,6 +77,86 @@ const useToast = () => {
|
|
|
75
77
|
return context.toast;
|
|
76
78
|
};
|
|
77
79
|
|
|
80
|
+
// ============ THEME SYSTEM ============
|
|
81
|
+
const ThemeContext = createContext();
|
|
82
|
+
|
|
83
|
+
function ThemeProvider({ children, initialTheme = DEFAULT_THEME, initialColorMode = 'light' }) {
|
|
84
|
+
const [currentTheme, setCurrentTheme] = useState(initialTheme);
|
|
85
|
+
const [colorMode, setColorModeState] = useState(initialColorMode);
|
|
86
|
+
|
|
87
|
+
// Apply theme and color mode to document
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
document.documentElement.setAttribute('data-theme', currentTheme);
|
|
90
|
+
// Apply light/dark class
|
|
91
|
+
if (colorMode === 'dark') {
|
|
92
|
+
document.documentElement.classList.add('dark');
|
|
93
|
+
document.documentElement.classList.remove('light');
|
|
94
|
+
} else {
|
|
95
|
+
document.documentElement.classList.add('light');
|
|
96
|
+
document.documentElement.classList.remove('dark');
|
|
97
|
+
}
|
|
98
|
+
}, [currentTheme, colorMode]);
|
|
99
|
+
|
|
100
|
+
const setTheme = useCallback(async (themeId) => {
|
|
101
|
+
if (!themes[themeId]) return;
|
|
102
|
+
setCurrentTheme(themeId);
|
|
103
|
+
|
|
104
|
+
// Save to server
|
|
105
|
+
try {
|
|
106
|
+
await fetch('/api/auth/profile', {
|
|
107
|
+
method: 'PUT',
|
|
108
|
+
headers: { 'Content-Type': 'application/json' },
|
|
109
|
+
body: JSON.stringify({ theme: themeId })
|
|
110
|
+
});
|
|
111
|
+
} catch (err) {
|
|
112
|
+
console.error('Error saving theme:', err);
|
|
113
|
+
}
|
|
114
|
+
}, []);
|
|
115
|
+
|
|
116
|
+
const setColorMode = useCallback(async (mode) => {
|
|
117
|
+
if (!['light', 'dark'].includes(mode)) return;
|
|
118
|
+
setColorModeState(mode);
|
|
119
|
+
|
|
120
|
+
// Save to server
|
|
121
|
+
try {
|
|
122
|
+
await fetch('/api/auth/profile', {
|
|
123
|
+
method: 'PUT',
|
|
124
|
+
headers: { 'Content-Type': 'application/json' },
|
|
125
|
+
body: JSON.stringify({ colorMode: mode })
|
|
126
|
+
});
|
|
127
|
+
} catch (err) {
|
|
128
|
+
console.error('Error saving color mode:', err);
|
|
129
|
+
}
|
|
130
|
+
}, []);
|
|
131
|
+
|
|
132
|
+
const toggleColorMode = useCallback(() => {
|
|
133
|
+
setColorMode(colorMode === 'dark' ? 'light' : 'dark');
|
|
134
|
+
}, [colorMode, setColorMode]);
|
|
135
|
+
|
|
136
|
+
const value = useMemo(() => ({
|
|
137
|
+
theme: currentTheme,
|
|
138
|
+
themeConfig: getTheme(currentTheme),
|
|
139
|
+
setTheme,
|
|
140
|
+
themes,
|
|
141
|
+
colorMode,
|
|
142
|
+
setColorMode,
|
|
143
|
+
toggleColorMode,
|
|
144
|
+
isDark: colorMode === 'dark'
|
|
145
|
+
}), [currentTheme, setTheme, colorMode, setColorMode, toggleColorMode]);
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<ThemeContext.Provider value={value}>
|
|
149
|
+
{children}
|
|
150
|
+
</ThemeContext.Provider>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const useTheme = () => {
|
|
155
|
+
const context = useContext(ThemeContext);
|
|
156
|
+
if (!context) throw new Error('useTheme must be used within ThemeProvider');
|
|
157
|
+
return context;
|
|
158
|
+
};
|
|
159
|
+
|
|
78
160
|
// ============ GLOBAL SEARCH MODAL ============
|
|
79
161
|
function GlobalSearchModal({ isOpen, onClose, roadmap, onNavigate, t }) {
|
|
80
162
|
const [query, setQuery] = useState('');
|
|
@@ -237,9 +319,9 @@ function useKeyboardShortcuts(handlers) {
|
|
|
237
319
|
return;
|
|
238
320
|
}
|
|
239
321
|
|
|
240
|
-
// Number keys 1-
|
|
322
|
+
// Number keys 1-8 for tab navigation
|
|
241
323
|
if (!e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
242
|
-
if (e.key >= '1' && e.key <= '
|
|
324
|
+
if (e.key >= '1' && e.key <= '8') {
|
|
243
325
|
e.preventDefault();
|
|
244
326
|
handlers.onTabChange?.(parseInt(e.key) - 1);
|
|
245
327
|
return;
|
|
@@ -1207,6 +1289,9 @@ function App() {
|
|
|
1207
1289
|
const [loginError, setLoginError] = useState(null);
|
|
1208
1290
|
const [teamMembers, setTeamMembers] = useState([]); // Users for task assignment
|
|
1209
1291
|
|
|
1292
|
+
// Theme
|
|
1293
|
+
const { setTheme } = useTheme();
|
|
1294
|
+
|
|
1210
1295
|
// Confirmation modal state
|
|
1211
1296
|
const [confirmModal, setConfirmModal] = useState({
|
|
1212
1297
|
show: false,
|
|
@@ -1280,6 +1365,14 @@ function App() {
|
|
|
1280
1365
|
authEnabled: data.authEnabled,
|
|
1281
1366
|
user: data.user || null
|
|
1282
1367
|
});
|
|
1368
|
+
// Sync theme and color mode from user
|
|
1369
|
+
if (data.user?.theme) {
|
|
1370
|
+
document.documentElement.setAttribute('data-theme', data.user.theme);
|
|
1371
|
+
}
|
|
1372
|
+
if (data.user?.colorMode) {
|
|
1373
|
+
document.documentElement.classList.remove('light', 'dark');
|
|
1374
|
+
document.documentElement.classList.add(data.user.colorMode);
|
|
1375
|
+
}
|
|
1283
1376
|
if (data.authenticated || !data.authEnabled) {
|
|
1284
1377
|
loadRoadmap();
|
|
1285
1378
|
loadTeamMembers();
|
|
@@ -1307,6 +1400,14 @@ function App() {
|
|
|
1307
1400
|
authEnabled: true,
|
|
1308
1401
|
user: data.user
|
|
1309
1402
|
});
|
|
1403
|
+
// Sync theme and color mode from user
|
|
1404
|
+
if (data.user?.theme) {
|
|
1405
|
+
document.documentElement.setAttribute('data-theme', data.user.theme);
|
|
1406
|
+
}
|
|
1407
|
+
if (data.user?.colorMode) {
|
|
1408
|
+
document.documentElement.classList.remove('light', 'dark');
|
|
1409
|
+
document.documentElement.classList.add(data.user.colorMode);
|
|
1410
|
+
}
|
|
1310
1411
|
loadRoadmap();
|
|
1311
1412
|
} else {
|
|
1312
1413
|
setLoginError(data.error || 'Error de autenticacion');
|
|
@@ -1339,8 +1440,31 @@ function App() {
|
|
|
1339
1440
|
return;
|
|
1340
1441
|
}
|
|
1341
1442
|
}
|
|
1342
|
-
if (!response.ok) throw new Error('No se pudo cargar roadmap.json');
|
|
1343
1443
|
const data = await response.json();
|
|
1444
|
+
// Handle case when roadmap.json doesn't exist - redirect to setup
|
|
1445
|
+
if (data.exists === false || data.redirectToSetup) {
|
|
1446
|
+
setRoadmap({ project_info: { name: '', description: '', stack: [] }, features: [] });
|
|
1447
|
+
setActiveTab('setup');
|
|
1448
|
+
setLoading(false);
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
if (!response.ok) throw new Error('No se pudo cargar roadmap.json');
|
|
1452
|
+
// Recalculate progress on load to ensure consistency
|
|
1453
|
+
if (data.features) {
|
|
1454
|
+
data.features.forEach(feature => {
|
|
1455
|
+
if (feature.tasks?.length > 0) {
|
|
1456
|
+
const completed = feature.tasks.filter(t => t.status === 'completed').length;
|
|
1457
|
+
feature.progress = Math.round((completed / feature.tasks.length) * 100);
|
|
1458
|
+
} else {
|
|
1459
|
+
feature.progress = 0;
|
|
1460
|
+
}
|
|
1461
|
+
});
|
|
1462
|
+
const allTasks = data.features.flatMap(f => f.tasks || []);
|
|
1463
|
+
if (allTasks.length > 0) {
|
|
1464
|
+
const totalCompleted = allTasks.filter(t => t.status === 'completed').length;
|
|
1465
|
+
data.project_info.total_progress = Math.round((totalCompleted / allTasks.length) * 100);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1344
1468
|
setRoadmap(data);
|
|
1345
1469
|
setHasChanges(false);
|
|
1346
1470
|
} catch (err) {
|
|
@@ -1597,13 +1721,13 @@ function App() {
|
|
|
1597
1721
|
if (error) {
|
|
1598
1722
|
return (
|
|
1599
1723
|
<div className="min-h-screen flex items-center justify-center bg-black p-4">
|
|
1600
|
-
<div className="
|
|
1724
|
+
<div className="theme-card p-8 max-w-md w-full">
|
|
1601
1725
|
<div className="flex items-center gap-3 mb-6">
|
|
1602
|
-
<div className="led led-
|
|
1726
|
+
<div className="led theme-led theme-led-danger" />
|
|
1603
1727
|
<span className="font-mono text-alert text-sm tracking-wider">ERROR</span>
|
|
1604
1728
|
</div>
|
|
1605
1729
|
<p className="font-mono text-gray-400 text-sm mb-6">{error}</p>
|
|
1606
|
-
<button onClick={loadRoadmap} className="btn-
|
|
1730
|
+
<button onClick={loadRoadmap} className="theme-btn-primary w-full">
|
|
1607
1731
|
RETRY
|
|
1608
1732
|
</button>
|
|
1609
1733
|
</div>
|
|
@@ -1621,7 +1745,7 @@ function App() {
|
|
|
1621
1745
|
{/* <div className="scanlines" /> */}
|
|
1622
1746
|
|
|
1623
1747
|
{/* Sidebar */}
|
|
1624
|
-
<aside className={`fixed left-0 top-0 h-full sidebar
|
|
1748
|
+
<aside className={`fixed left-0 top-0 h-full theme-sidebar z-50 transition-all duration-300 ${sidebarCollapsed ? 'w-16' : 'w-64'}`}>
|
|
1625
1749
|
<div className="flex flex-col h-full">
|
|
1626
1750
|
{/* Logo */}
|
|
1627
1751
|
<div className="p-4 border-b border-matrix/10">
|
|
@@ -1646,8 +1770,8 @@ function App() {
|
|
|
1646
1770
|
<span className="font-mono text-[10px] text-gray-500 tracking-widest">{language === 'es' ? 'PROGRESO' : 'PROGRESS'}</span>
|
|
1647
1771
|
<span className="font-display text-2xl text-matrix">{projectInfo.total_progress || 0}%</span>
|
|
1648
1772
|
</div>
|
|
1649
|
-
<div className="progress
|
|
1650
|
-
<div className="progress-
|
|
1773
|
+
<div className="theme-progress">
|
|
1774
|
+
<div className="theme-progress-fill" style={{ width: `${projectInfo.total_progress || 0}%` }} />
|
|
1651
1775
|
</div>
|
|
1652
1776
|
</div>
|
|
1653
1777
|
<div className="grid grid-cols-3 gap-2 text-center">
|
|
@@ -1676,7 +1800,7 @@ function App() {
|
|
|
1676
1800
|
<button
|
|
1677
1801
|
key={item.id}
|
|
1678
1802
|
onClick={() => setActiveTab(item.id)}
|
|
1679
|
-
className={`nav-
|
|
1803
|
+
className={`theme-nav-item w-full flex items-center gap-3 px-4 py-3 transition-all ${
|
|
1680
1804
|
isActive ? 'active' : 'text-gray-500 hover:text-gray-300 hover:bg-white/[0.02]'
|
|
1681
1805
|
} ${sidebarCollapsed ? 'justify-center px-2' : 'pl-8'}`}
|
|
1682
1806
|
>
|
|
@@ -1699,7 +1823,7 @@ function App() {
|
|
|
1699
1823
|
</div>
|
|
1700
1824
|
)}
|
|
1701
1825
|
{hasChanges && !sidebarCollapsed && (
|
|
1702
|
-
<button onClick={saveRoadmap} disabled={saving} className="btn-
|
|
1826
|
+
<button onClick={saveRoadmap} disabled={saving} className="theme-btn-primary w-full flex items-center justify-center gap-2">
|
|
1703
1827
|
{saving ? <Loader2 className="w-4 h-4 animate-spin" /> : <Save className="w-4 h-4" />}
|
|
1704
1828
|
{t('common.save')}
|
|
1705
1829
|
</button>
|
|
@@ -1772,7 +1896,7 @@ function App() {
|
|
|
1772
1896
|
</header>
|
|
1773
1897
|
|
|
1774
1898
|
{/* Content Area */}
|
|
1775
|
-
<div className="p-6 pb-24 grid-bg min-h-[calc(100vh-120px)]">
|
|
1899
|
+
<div className="p-6 pb-24 theme-grid-bg min-h-[calc(100vh-120px)]">
|
|
1776
1900
|
{activeTab === 'features' && (
|
|
1777
1901
|
<FeaturesTab
|
|
1778
1902
|
features={filteredFeatures}
|
|
@@ -2256,7 +2380,7 @@ function AIGeneratorModal({ roadmap, setRoadmap, setHasChanges, onClose }) {
|
|
|
2256
2380
|
- Notificaciones por email
|
|
2257
2381
|
- Integración con Stripe para pagos
|
|
2258
2382
|
- API REST documentada con Swagger`}
|
|
2259
|
-
className="w-full h-64 input
|
|
2383
|
+
className="w-full h-64 theme-input p-4 text-sm resize-none"
|
|
2260
2384
|
spellCheck={false}
|
|
2261
2385
|
/>
|
|
2262
2386
|
<p className="mt-2 font-mono text-[10px] text-gray-600">
|
|
@@ -2268,7 +2392,7 @@ function AIGeneratorModal({ roadmap, setRoadmap, setHasChanges, onClose }) {
|
|
|
2268
2392
|
<button
|
|
2269
2393
|
onClick={analyzeWithAI}
|
|
2270
2394
|
disabled={analyzing || !requirements.trim() || (!claudeStatus.available && !claudeStatus.checking)}
|
|
2271
|
-
className="btn-
|
|
2395
|
+
className="theme-btn-primary flex-1 py-3 disabled:opacity-50 flex items-center justify-center gap-2"
|
|
2272
2396
|
>
|
|
2273
2397
|
<Sparkles className="w-4 h-4" />
|
|
2274
2398
|
GENERAR CON IA
|
|
@@ -2350,7 +2474,7 @@ function AIGeneratorModal({ roadmap, setRoadmap, setHasChanges, onClose }) {
|
|
|
2350
2474
|
<div className="flex gap-3">
|
|
2351
2475
|
<button
|
|
2352
2476
|
onClick={mergeResults}
|
|
2353
|
-
className="btn-
|
|
2477
|
+
className="theme-btn-primary flex-1 py-3 flex items-center justify-center gap-2"
|
|
2354
2478
|
>
|
|
2355
2479
|
<Plus className="w-4 h-4" />
|
|
2356
2480
|
AÑADIR AL ROADMAP
|
|
@@ -2533,13 +2657,13 @@ function FeaturesTab({
|
|
|
2533
2657
|
placeholder={t ? t('common.search') : "search tasks..."}
|
|
2534
2658
|
value={searchTerm}
|
|
2535
2659
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
2536
|
-
className="input
|
|
2660
|
+
className="theme-input w-full pl-10 pr-4 py-2.5 text-sm"
|
|
2537
2661
|
/>
|
|
2538
2662
|
</div>
|
|
2539
2663
|
<select
|
|
2540
2664
|
value={statusFilter}
|
|
2541
2665
|
onChange={(e) => setStatusFilter(e.target.value)}
|
|
2542
|
-
className="input
|
|
2666
|
+
className="theme-input px-4 py-2.5 text-sm cursor-pointer"
|
|
2543
2667
|
>
|
|
2544
2668
|
<option value="all">{language === 'es' ? 'TODOS' : 'ALL STATUS'}</option>
|
|
2545
2669
|
<option value="pending">{language === 'es' ? 'PENDIENTE' : 'PENDING'}</option>
|
|
@@ -2568,17 +2692,17 @@ function FeaturesTab({
|
|
|
2568
2692
|
{/* Advanced Filters Toggle */}
|
|
2569
2693
|
<button
|
|
2570
2694
|
onClick={() => setShowFilters(!showFilters)}
|
|
2571
|
-
className={`btn-
|
|
2695
|
+
className={`theme-btn-primary flex items-center gap-2 ${showFilters ? 'border-cyber/50 text-cyber' : ''}`}
|
|
2572
2696
|
>
|
|
2573
2697
|
<Filter className="w-4 h-4" />
|
|
2574
2698
|
{language === 'es' ? 'FILTROS' : 'FILTERS'}
|
|
2575
2699
|
</button>
|
|
2576
2700
|
|
|
2577
|
-
<button onClick={() => setShowAddFeature(true)} className="btn-
|
|
2701
|
+
<button onClick={() => setShowAddFeature(true)} className="theme-btn-primary flex items-center gap-2">
|
|
2578
2702
|
<Plus className="w-4 h-4" />
|
|
2579
2703
|
{language === 'es' ? 'NUEVA FEATURE' : 'NEW FEATURE'}
|
|
2580
2704
|
</button>
|
|
2581
|
-
<button onClick={() => setShowAIGenerator(true)} className="btn-
|
|
2705
|
+
<button onClick={() => setShowAIGenerator(true)} className="theme-btn-primary flex items-center gap-2 bg-gradient-to-r from-cyber/20 to-matrix/20 border-cyber/50 hover:border-cyber">
|
|
2582
2706
|
<Sparkles className="w-4 h-4 text-cyber" />
|
|
2583
2707
|
AI
|
|
2584
2708
|
</button>
|
|
@@ -2592,7 +2716,7 @@ function FeaturesTab({
|
|
|
2592
2716
|
<select
|
|
2593
2717
|
value={priorityFilter}
|
|
2594
2718
|
onChange={(e) => setPriorityFilter(e.target.value)}
|
|
2595
|
-
className="input
|
|
2719
|
+
className="theme-input px-3 py-1.5 text-xs cursor-pointer"
|
|
2596
2720
|
>
|
|
2597
2721
|
<option value="all">{language === 'es' ? 'TODAS' : 'ALL'}</option>
|
|
2598
2722
|
<option value="high">{language === 'es' ? 'ALTA' : 'HIGH'}</option>
|
|
@@ -2605,7 +2729,7 @@ function FeaturesTab({
|
|
|
2605
2729
|
<select
|
|
2606
2730
|
value={assigneeFilter}
|
|
2607
2731
|
onChange={(e) => setAssigneeFilter(e.target.value)}
|
|
2608
|
-
className="input
|
|
2732
|
+
className="theme-input px-3 py-1.5 text-xs cursor-pointer"
|
|
2609
2733
|
>
|
|
2610
2734
|
<option value="all">{language === 'es' ? 'TODOS' : 'ALL'}</option>
|
|
2611
2735
|
<option value="">{language === 'es' ? 'SIN ASIGNAR' : 'UNASSIGNED'}</option>
|
|
@@ -2654,7 +2778,7 @@ function FeaturesTab({
|
|
|
2654
2778
|
)}
|
|
2655
2779
|
|
|
2656
2780
|
{features.length === 0 && viewMode === 'list' && (
|
|
2657
|
-
<div className="
|
|
2781
|
+
<div className="theme-card p-12 text-center">
|
|
2658
2782
|
<Search className="w-10 h-10 mx-auto mb-4 text-gray-700" />
|
|
2659
2783
|
<p className="font-mono text-gray-600 text-sm">{language === 'es' ? 'NO HAY TAREAS' : 'NO TASKS FOUND'}</p>
|
|
2660
2784
|
</div>
|
|
@@ -2684,7 +2808,7 @@ function FeatureCard({ feature, onUpdateTaskStatus, onAddTask, showAddTask, setS
|
|
|
2684
2808
|
{/* Progress Ring */}
|
|
2685
2809
|
<div className="relative w-14 h-14 flex-shrink-0">
|
|
2686
2810
|
<svg className="w-full h-full -rotate-90" viewBox="0 0 36 36">
|
|
2687
|
-
<circle cx="18" cy="18" r="15" fill="none" stroke="
|
|
2811
|
+
<circle cx="18" cy="18" r="15" fill="none" stroke="var(--bg-secondary)" strokeWidth="3" />
|
|
2688
2812
|
<circle
|
|
2689
2813
|
cx="18" cy="18" r="15" fill="none"
|
|
2690
2814
|
stroke="url(#progressGradient)"
|
|
@@ -2695,8 +2819,8 @@ function FeatureCard({ feature, onUpdateTaskStatus, onAddTask, showAddTask, setS
|
|
|
2695
2819
|
/>
|
|
2696
2820
|
<defs>
|
|
2697
2821
|
<linearGradient id="progressGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
2698
|
-
<stop offset="0%" stopColor="
|
|
2699
|
-
<stop offset="100%" stopColor="
|
|
2822
|
+
<stop offset="0%" stopColor="var(--accent-primary)" />
|
|
2823
|
+
<stop offset="100%" stopColor="var(--accent-tertiary)" />
|
|
2700
2824
|
</linearGradient>
|
|
2701
2825
|
</defs>
|
|
2702
2826
|
</svg>
|
|
@@ -2726,8 +2850,8 @@ function FeatureCard({ feature, onUpdateTaskStatus, onAddTask, showAddTask, setS
|
|
|
2726
2850
|
<span>{feature.tasks.filter(t => t.status === 'completed').length} / {feature.tasks.length} TASKS</span>
|
|
2727
2851
|
<span>{feature.progress}%</span>
|
|
2728
2852
|
</div>
|
|
2729
|
-
<div className="progress
|
|
2730
|
-
<div className="progress-
|
|
2853
|
+
<div className="theme-progress">
|
|
2854
|
+
<div className="theme-progress-fill" style={{ width: `${feature.progress}%` }} />
|
|
2731
2855
|
</div>
|
|
2732
2856
|
</div>
|
|
2733
2857
|
|
|
@@ -2764,8 +2888,8 @@ function TaskList({ tasks = [], featureId = '', onUpdateStatus, team = [], onAss
|
|
|
2764
2888
|
};
|
|
2765
2889
|
|
|
2766
2890
|
const statusConfig = {
|
|
2767
|
-
completed: { led: 'led-
|
|
2768
|
-
in_progress: { led: 'led-
|
|
2891
|
+
completed: { led: 'theme-led theme-led-success', label: 'DONE', color: 'text-matrix' },
|
|
2892
|
+
in_progress: { led: 'theme-led theme-led-warning', label: 'ACTIVE', color: 'text-signal' },
|
|
2769
2893
|
pending: { led: 'led-gray', label: 'QUEUE', color: 'text-gray-500' }
|
|
2770
2894
|
};
|
|
2771
2895
|
|
|
@@ -2970,7 +3094,7 @@ function TaskList({ tasks = [], featureId = '', onUpdateStatus, team = [], onAss
|
|
|
2970
3094
|
const member = team.find(m => m.id === e.target.value);
|
|
2971
3095
|
onAssignTask?.(featureId, task.id, e.target.value, member?.name || '');
|
|
2972
3096
|
}}
|
|
2973
|
-
className="input
|
|
3097
|
+
className="theme-input px-3 py-1.5 text-[11px]"
|
|
2974
3098
|
>
|
|
2975
3099
|
<option value="">Sin asignar</option>
|
|
2976
3100
|
{team.map((member) => (
|
|
@@ -3063,27 +3187,27 @@ function MetricsTab({ roadmap, language }) {
|
|
|
3063
3187
|
<div className="space-y-6 animate-fade-in">
|
|
3064
3188
|
{/* Summary Cards */}
|
|
3065
3189
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
3066
|
-
<div className="
|
|
3190
|
+
<div className="theme-card p-5">
|
|
3067
3191
|
<div className="font-mono text-[10px] text-gray-500 mb-2">{language === 'es' ? 'TOTAL TAREAS' : 'TOTAL TASKS'}</div>
|
|
3068
3192
|
<div className="font-display text-3xl text-white">{stats.total}</div>
|
|
3069
3193
|
</div>
|
|
3070
|
-
<div className="
|
|
3194
|
+
<div className="theme-card p-5 border-matrix/20">
|
|
3071
3195
|
<div className="font-mono text-[10px] text-gray-500 mb-2">{language === 'es' ? 'COMPLETADAS' : 'COMPLETED'}</div>
|
|
3072
3196
|
<div className="font-display text-3xl text-matrix">{stats.completed}</div>
|
|
3073
3197
|
<div className="font-mono text-[10px] text-matrix/60 mt-1">{completionRate}%</div>
|
|
3074
3198
|
</div>
|
|
3075
|
-
<div className="
|
|
3199
|
+
<div className="theme-card p-5 border-signal/20">
|
|
3076
3200
|
<div className="font-mono text-[10px] text-gray-500 mb-2">{language === 'es' ? 'EN PROGRESO' : 'IN PROGRESS'}</div>
|
|
3077
3201
|
<div className="font-display text-3xl text-signal">{stats.inProgress}</div>
|
|
3078
3202
|
</div>
|
|
3079
|
-
<div className="
|
|
3203
|
+
<div className="theme-card p-5">
|
|
3080
3204
|
<div className="font-mono text-[10px] text-gray-500 mb-2">{language === 'es' ? 'PENDIENTES' : 'PENDING'}</div>
|
|
3081
3205
|
<div className="font-display text-3xl text-gray-500">{stats.pending}</div>
|
|
3082
3206
|
</div>
|
|
3083
3207
|
</div>
|
|
3084
3208
|
|
|
3085
3209
|
{/* Progress Overview */}
|
|
3086
|
-
<div className="
|
|
3210
|
+
<div className="theme-card p-5">
|
|
3087
3211
|
<h3 className="font-mono text-sm text-white mb-4">{language === 'es' ? 'PROGRESO GENERAL' : 'OVERALL PROGRESS'}</h3>
|
|
3088
3212
|
<div className="relative h-8 bg-void-100 border border-white/5 overflow-hidden">
|
|
3089
3213
|
<div
|
|
@@ -3104,7 +3228,7 @@ function MetricsTab({ roadmap, language }) {
|
|
|
3104
3228
|
</div>
|
|
3105
3229
|
|
|
3106
3230
|
{/* By Priority */}
|
|
3107
|
-
<div className="
|
|
3231
|
+
<div className="theme-card p-5">
|
|
3108
3232
|
<h3 className="font-mono text-sm text-white mb-4">{language === 'es' ? 'POR PRIORIDAD' : 'BY PRIORITY'}</h3>
|
|
3109
3233
|
<div className="grid grid-cols-3 gap-4">
|
|
3110
3234
|
<div className="p-4 bg-alert/5 border border-alert/20">
|
|
@@ -3132,7 +3256,7 @@ function MetricsTab({ roadmap, language }) {
|
|
|
3132
3256
|
</div>
|
|
3133
3257
|
|
|
3134
3258
|
{/* By Feature */}
|
|
3135
|
-
<div className="
|
|
3259
|
+
<div className="theme-card p-5">
|
|
3136
3260
|
<h3 className="font-mono text-sm text-white mb-4">{language === 'es' ? 'POR FEATURE' : 'BY FEATURE'}</h3>
|
|
3137
3261
|
<div className="space-y-3">
|
|
3138
3262
|
{stats.byFeature.map((feature, idx) => (
|
|
@@ -3156,7 +3280,7 @@ function MetricsTab({ roadmap, language }) {
|
|
|
3156
3280
|
</div>
|
|
3157
3281
|
|
|
3158
3282
|
{/* Export Options */}
|
|
3159
|
-
<div className="
|
|
3283
|
+
<div className="theme-card p-5">
|
|
3160
3284
|
<h3 className="font-mono text-sm text-white mb-4">{language === 'es' ? 'EXPORTAR DATOS' : 'EXPORT DATA'}</h3>
|
|
3161
3285
|
<div className="flex gap-3">
|
|
3162
3286
|
<button
|
|
@@ -3176,7 +3300,7 @@ function MetricsTab({ roadmap, language }) {
|
|
|
3176
3300
|
a.download = 'roadmap-export.csv';
|
|
3177
3301
|
a.click();
|
|
3178
3302
|
}}
|
|
3179
|
-
className="btn-
|
|
3303
|
+
className="theme-btn-primary flex items-center gap-2"
|
|
3180
3304
|
>
|
|
3181
3305
|
<FileDown className="w-4 h-4" />
|
|
3182
3306
|
CSV
|
|
@@ -3190,7 +3314,7 @@ function MetricsTab({ roadmap, language }) {
|
|
|
3190
3314
|
a.download = 'roadmap-backup.json';
|
|
3191
3315
|
a.click();
|
|
3192
3316
|
}}
|
|
3193
|
-
className="btn-
|
|
3317
|
+
className="theme-btn-primary flex items-center gap-2"
|
|
3194
3318
|
>
|
|
3195
3319
|
<FileDown className="w-4 h-4" />
|
|
3196
3320
|
JSON
|
|
@@ -3216,23 +3340,23 @@ function ResourcesTab({ resources }) {
|
|
|
3216
3340
|
<div className="space-y-6 animate-fade-in">
|
|
3217
3341
|
{/* Summary */}
|
|
3218
3342
|
<div className="grid grid-cols-3 gap-4">
|
|
3219
|
-
<div className="
|
|
3343
|
+
<div className="theme-card p-5">
|
|
3220
3344
|
<div className="flex items-center gap-3 mb-3">
|
|
3221
|
-
<div className="led led-
|
|
3345
|
+
<div className="led theme-led theme-led-success" />
|
|
3222
3346
|
<span className="font-mono text-[10px] text-gray-500 tracking-wider">UI COMPONENTS</span>
|
|
3223
3347
|
</div>
|
|
3224
3348
|
<div className="metric-display text-3xl text-matrix">{ui_components.length}</div>
|
|
3225
3349
|
</div>
|
|
3226
|
-
<div className="
|
|
3350
|
+
<div className="theme-card p-5">
|
|
3227
3351
|
<div className="flex items-center gap-3 mb-3">
|
|
3228
|
-
<div className="led led-
|
|
3352
|
+
<div className="led theme-led theme-led-warning" />
|
|
3229
3353
|
<span className="font-mono text-[10px] text-gray-500 tracking-wider">UTILITIES</span>
|
|
3230
3354
|
</div>
|
|
3231
3355
|
<div className="metric-display text-3xl text-signal">{utilities.length}</div>
|
|
3232
3356
|
</div>
|
|
3233
|
-
<div className="
|
|
3357
|
+
<div className="theme-card p-5">
|
|
3234
3358
|
<div className="flex items-center gap-3 mb-3">
|
|
3235
|
-
<div className="led led-
|
|
3359
|
+
<div className="led theme-led theme-led-success" />
|
|
3236
3360
|
<span className="font-mono text-[10px] text-gray-500 tracking-wider">DB TABLES</span>
|
|
3237
3361
|
</div>
|
|
3238
3362
|
<div className="metric-display text-3xl text-cyber">{database_tables.length}</div>
|
|
@@ -3241,7 +3365,7 @@ function ResourcesTab({ resources }) {
|
|
|
3241
3365
|
|
|
3242
3366
|
{/* UI Components */}
|
|
3243
3367
|
{ui_components.length > 0 && (
|
|
3244
|
-
<div className="
|
|
3368
|
+
<div className="theme-card p-5">
|
|
3245
3369
|
<h3 className="font-mono text-sm text-matrix tracking-wider mb-4"># UI_COMPONENTS</h3>
|
|
3246
3370
|
<div className="space-y-3">
|
|
3247
3371
|
{ui_components.map((comp, idx) => (
|
|
@@ -3265,7 +3389,7 @@ function ResourcesTab({ resources }) {
|
|
|
3265
3389
|
|
|
3266
3390
|
{/* Utilities */}
|
|
3267
3391
|
{utilities.length > 0 && (
|
|
3268
|
-
<div className="
|
|
3392
|
+
<div className="theme-card p-5">
|
|
3269
3393
|
<h3 className="font-mono text-sm text-signal tracking-wider mb-4"># UTILITIES</h3>
|
|
3270
3394
|
<div className="space-y-3">
|
|
3271
3395
|
{utilities.map((util, idx) => (
|
|
@@ -3302,7 +3426,7 @@ function ResourcesTab({ resources }) {
|
|
|
3302
3426
|
|
|
3303
3427
|
{/* Database Tables */}
|
|
3304
3428
|
{database_tables.length > 0 && (
|
|
3305
|
-
<div className="
|
|
3429
|
+
<div className="theme-card p-5">
|
|
3306
3430
|
<h3 className="font-mono text-sm text-cyber tracking-wider mb-4"># DATABASE_TABLES</h3>
|
|
3307
3431
|
<div className="space-y-3">
|
|
3308
3432
|
{database_tables.map((table, idx) => (
|
|
@@ -3369,8 +3493,8 @@ function DebtTab({ roadmap, setRoadmap, setHasChanges }) {
|
|
|
3369
3493
|
|
|
3370
3494
|
const statusConfig = {
|
|
3371
3495
|
pending: { led: 'led-gray', label: 'PENDING', color: 'text-gray-500' },
|
|
3372
|
-
in_progress: { led: 'led-
|
|
3373
|
-
resolved: { led: 'led-
|
|
3496
|
+
in_progress: { led: 'theme-led theme-led-warning', label: 'IN PROGRESS', color: 'text-signal' },
|
|
3497
|
+
resolved: { led: 'theme-led theme-led-success', label: 'RESOLVED', color: 'text-matrix' }
|
|
3374
3498
|
};
|
|
3375
3499
|
|
|
3376
3500
|
// Update debt status
|
|
@@ -3458,30 +3582,30 @@ function DebtTab({ roadmap, setRoadmap, setHasChanges }) {
|
|
|
3458
3582
|
<div className="grid grid-cols-3 gap-4">
|
|
3459
3583
|
<button
|
|
3460
3584
|
onClick={() => setFilterSeverity(filterSeverity === 'high' ? 'all' : 'high')}
|
|
3461
|
-
className={`
|
|
3585
|
+
className={`theme-card p-5 transition-all ${filterSeverity === 'high' ? 'border-alert glow-alert' : 'border-alert/30 hover:border-alert/50'}`}
|
|
3462
3586
|
>
|
|
3463
3587
|
<div className="flex items-center gap-3 mb-3">
|
|
3464
|
-
<div className="led led-
|
|
3588
|
+
<div className="led theme-led theme-led-danger" />
|
|
3465
3589
|
<span className="font-mono text-[10px] text-gray-500 tracking-wider">HIGH</span>
|
|
3466
3590
|
</div>
|
|
3467
3591
|
<div className="metric-display text-3xl text-alert">{bySeverity.high.length}</div>
|
|
3468
3592
|
</button>
|
|
3469
3593
|
<button
|
|
3470
3594
|
onClick={() => setFilterSeverity(filterSeverity === 'medium' ? 'all' : 'medium')}
|
|
3471
|
-
className={`
|
|
3595
|
+
className={`theme-card p-5 transition-all ${filterSeverity === 'medium' ? 'border-signal glow-signal' : 'border-signal/30 hover:border-signal/50'}`}
|
|
3472
3596
|
>
|
|
3473
3597
|
<div className="flex items-center gap-3 mb-3">
|
|
3474
|
-
<div className="led led-
|
|
3598
|
+
<div className="led theme-led theme-led-warning" />
|
|
3475
3599
|
<span className="font-mono text-[10px] text-gray-500 tracking-wider">MEDIUM</span>
|
|
3476
3600
|
</div>
|
|
3477
3601
|
<div className="metric-display text-3xl text-signal">{bySeverity.medium.length}</div>
|
|
3478
3602
|
</button>
|
|
3479
3603
|
<button
|
|
3480
3604
|
onClick={() => setFilterSeverity(filterSeverity === 'low' ? 'all' : 'low')}
|
|
3481
|
-
className={`
|
|
3605
|
+
className={`theme-card p-5 transition-all ${filterSeverity === 'low' ? 'border-cyber glow-cyber' : 'border-cyber/30 hover:border-cyber/50'}`}
|
|
3482
3606
|
>
|
|
3483
3607
|
<div className="flex items-center gap-3 mb-3">
|
|
3484
|
-
<div className="led led-
|
|
3608
|
+
<div className="led theme-led theme-led-success" />
|
|
3485
3609
|
<span className="font-mono text-[10px] text-gray-500 tracking-wider">LOW</span>
|
|
3486
3610
|
</div>
|
|
3487
3611
|
<div className="metric-display text-3xl text-cyber">{bySeverity.low.length}</div>
|
|
@@ -3494,7 +3618,7 @@ function DebtTab({ roadmap, setRoadmap, setHasChanges }) {
|
|
|
3494
3618
|
<select
|
|
3495
3619
|
value={filterStatus}
|
|
3496
3620
|
onChange={(e) => setFilterStatus(e.target.value)}
|
|
3497
|
-
className="input
|
|
3621
|
+
className="theme-input px-3 py-2 text-xs"
|
|
3498
3622
|
>
|
|
3499
3623
|
<option value="all">ALL STATUS</option>
|
|
3500
3624
|
<option value="pending">PENDING</option>
|
|
@@ -3514,7 +3638,7 @@ function DebtTab({ roadmap, setRoadmap, setHasChanges }) {
|
|
|
3514
3638
|
</span>
|
|
3515
3639
|
<button
|
|
3516
3640
|
onClick={() => setShowAddDebt(true)}
|
|
3517
|
-
className="ml-auto btn-
|
|
3641
|
+
className="ml-auto theme-btn-primary flex items-center gap-2"
|
|
3518
3642
|
>
|
|
3519
3643
|
<Plus className="w-4 h-4" />
|
|
3520
3644
|
ADD DEBT
|
|
@@ -3533,7 +3657,7 @@ function DebtTab({ roadmap, setRoadmap, setHasChanges }) {
|
|
|
3533
3657
|
|
|
3534
3658
|
{/* Debt Items */}
|
|
3535
3659
|
{filteredDebts.length > 0 ? (
|
|
3536
|
-
<div className="
|
|
3660
|
+
<div className="theme-card p-5">
|
|
3537
3661
|
<h3 className="font-mono text-sm text-signal tracking-wider mb-4"># TECHNICAL_DEBT</h3>
|
|
3538
3662
|
<div className="space-y-2">
|
|
3539
3663
|
{filteredDebts.map((debt) => {
|
|
@@ -3734,7 +3858,7 @@ function DebtTab({ roadmap, setRoadmap, setHasChanges }) {
|
|
|
3734
3858
|
</div>
|
|
3735
3859
|
</div>
|
|
3736
3860
|
) : (
|
|
3737
|
-
<div className="
|
|
3861
|
+
<div className="theme-card p-12 text-center">
|
|
3738
3862
|
<CheckCircle2 className="w-10 h-10 mx-auto mb-4 text-matrix" />
|
|
3739
3863
|
<p className="font-mono text-sm text-gray-600">
|
|
3740
3864
|
{allDebts.length === 0 ? 'NO TECHNICAL DEBT REGISTERED' : 'NO ITEMS MATCH FILTERS'}
|
|
@@ -3742,7 +3866,7 @@ function DebtTab({ roadmap, setRoadmap, setHasChanges }) {
|
|
|
3742
3866
|
{allDebts.length === 0 && (
|
|
3743
3867
|
<button
|
|
3744
3868
|
onClick={() => setShowAddDebt(true)}
|
|
3745
|
-
className="btn-
|
|
3869
|
+
className="theme-btn-primary mt-4 inline-flex items-center gap-2"
|
|
3746
3870
|
>
|
|
3747
3871
|
<Plus className="w-4 h-4" />
|
|
3748
3872
|
ADD FIRST DEBT
|
|
@@ -3752,7 +3876,7 @@ function DebtTab({ roadmap, setRoadmap, setHasChanges }) {
|
|
|
3752
3876
|
)}
|
|
3753
3877
|
|
|
3754
3878
|
{/* Debt Schema Info */}
|
|
3755
|
-
<div className="
|
|
3879
|
+
<div className="theme-card p-4">
|
|
3756
3880
|
<details>
|
|
3757
3881
|
<summary className="font-mono text-[10px] text-gray-600 cursor-pointer hover:text-white">
|
|
3758
3882
|
JSON SCHEMA FOR TECHNICAL DEBT
|
|
@@ -3826,7 +3950,7 @@ function DebtForm({ features, team, initialData, onSave, onCancel, isEdit }) {
|
|
|
3826
3950
|
};
|
|
3827
3951
|
|
|
3828
3952
|
return (
|
|
3829
|
-
<form onSubmit={handleSubmit} className="
|
|
3953
|
+
<form onSubmit={handleSubmit} className="theme-card p-5 space-y-4">
|
|
3830
3954
|
<h3 className="font-mono text-sm text-signal tracking-wider">
|
|
3831
3955
|
{isEdit ? '# EDIT DEBT' : '# NEW TECHNICAL DEBT'}
|
|
3832
3956
|
</h3>
|
|
@@ -3838,7 +3962,7 @@ function DebtForm({ features, team, initialData, onSave, onCancel, isEdit }) {
|
|
|
3838
3962
|
<select
|
|
3839
3963
|
value={featureId}
|
|
3840
3964
|
onChange={(e) => { setFeatureId(e.target.value); setTaskId(''); }}
|
|
3841
|
-
className="input
|
|
3965
|
+
className="theme-input w-full px-3 py-2 text-sm"
|
|
3842
3966
|
required
|
|
3843
3967
|
>
|
|
3844
3968
|
<option value="">Select feature...</option>
|
|
@@ -3852,7 +3976,7 @@ function DebtForm({ features, team, initialData, onSave, onCancel, isEdit }) {
|
|
|
3852
3976
|
<select
|
|
3853
3977
|
value={taskId}
|
|
3854
3978
|
onChange={(e) => setTaskId(e.target.value)}
|
|
3855
|
-
className="input
|
|
3979
|
+
className="theme-input w-full px-3 py-2 text-sm"
|
|
3856
3980
|
required
|
|
3857
3981
|
disabled={!featureId}
|
|
3858
3982
|
>
|
|
@@ -3872,7 +3996,7 @@ function DebtForm({ features, team, initialData, onSave, onCancel, isEdit }) {
|
|
|
3872
3996
|
onChange={(e) => setDescription(e.target.value)}
|
|
3873
3997
|
placeholder="Describe the technical debt..."
|
|
3874
3998
|
rows={2}
|
|
3875
|
-
className="input
|
|
3999
|
+
className="theme-input w-full px-3 py-2 text-sm resize-none"
|
|
3876
4000
|
required
|
|
3877
4001
|
/>
|
|
3878
4002
|
</div>
|
|
@@ -3880,7 +4004,7 @@ function DebtForm({ features, team, initialData, onSave, onCancel, isEdit }) {
|
|
|
3880
4004
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
|
3881
4005
|
<div>
|
|
3882
4006
|
<label className="font-mono text-[10px] text-gray-500 tracking-wider mb-2 block">SEVERITY</label>
|
|
3883
|
-
<select value={severity} onChange={(e) => setSeverity(e.target.value)} className="input
|
|
4007
|
+
<select value={severity} onChange={(e) => setSeverity(e.target.value)} className="theme-input w-full px-3 py-2 text-sm">
|
|
3884
4008
|
<option value="high">HIGH</option>
|
|
3885
4009
|
<option value="medium">MEDIUM</option>
|
|
3886
4010
|
<option value="low">LOW</option>
|
|
@@ -3888,7 +4012,7 @@ function DebtForm({ features, team, initialData, onSave, onCancel, isEdit }) {
|
|
|
3888
4012
|
</div>
|
|
3889
4013
|
<div>
|
|
3890
4014
|
<label className="font-mono text-[10px] text-gray-500 tracking-wider mb-2 block">PRIORITY</label>
|
|
3891
|
-
<select value={priority} onChange={(e) => setPriority(e.target.value)} className="input
|
|
4015
|
+
<select value={priority} onChange={(e) => setPriority(e.target.value)} className="theme-input w-full px-3 py-2 text-sm">
|
|
3892
4016
|
<option value="critical">CRITICAL</option>
|
|
3893
4017
|
<option value="high">HIGH</option>
|
|
3894
4018
|
<option value="medium">MEDIUM</option>
|
|
@@ -3902,12 +4026,12 @@ function DebtForm({ features, team, initialData, onSave, onCancel, isEdit }) {
|
|
|
3902
4026
|
value={estimatedEffort}
|
|
3903
4027
|
onChange={(e) => setEstimatedEffort(e.target.value)}
|
|
3904
4028
|
placeholder="e.g. 2h, 1d"
|
|
3905
|
-
className="input
|
|
4029
|
+
className="theme-input w-full px-3 py-2 text-sm"
|
|
3906
4030
|
/>
|
|
3907
4031
|
</div>
|
|
3908
4032
|
<div>
|
|
3909
4033
|
<label className="font-mono text-[10px] text-gray-500 tracking-wider mb-2 block">ASSIGNED</label>
|
|
3910
|
-
<select value={assignedTo} onChange={(e) => setAssignedTo(e.target.value)} className="input
|
|
4034
|
+
<select value={assignedTo} onChange={(e) => setAssignedTo(e.target.value)} className="theme-input w-full px-3 py-2 text-sm">
|
|
3911
4035
|
<option value="">Unassigned</option>
|
|
3912
4036
|
{team.map(m => (
|
|
3913
4037
|
<option key={m.id} value={m.name}>{m.name}</option>
|
|
@@ -3923,7 +4047,7 @@ function DebtForm({ features, team, initialData, onSave, onCancel, isEdit }) {
|
|
|
3923
4047
|
onChange={(e) => setImpact(e.target.value)}
|
|
3924
4048
|
placeholder="What happens if not fixed?"
|
|
3925
4049
|
rows={2}
|
|
3926
|
-
className="input
|
|
4050
|
+
className="theme-input w-full px-3 py-2 text-sm resize-none"
|
|
3927
4051
|
/>
|
|
3928
4052
|
</div>
|
|
3929
4053
|
|
|
@@ -3934,7 +4058,7 @@ function DebtForm({ features, team, initialData, onSave, onCancel, isEdit }) {
|
|
|
3934
4058
|
onChange={(e) => setSolution(e.target.value)}
|
|
3935
4059
|
placeholder="How to fix this?"
|
|
3936
4060
|
rows={2}
|
|
3937
|
-
className="input
|
|
4061
|
+
className="theme-input w-full px-3 py-2 text-sm resize-none"
|
|
3938
4062
|
/>
|
|
3939
4063
|
</div>
|
|
3940
4064
|
|
|
@@ -3946,7 +4070,7 @@ function DebtForm({ features, team, initialData, onSave, onCancel, isEdit }) {
|
|
|
3946
4070
|
value={affectedFiles}
|
|
3947
4071
|
onChange={(e) => setAffectedFiles(e.target.value)}
|
|
3948
4072
|
placeholder="src/file1.js, src/file2.js"
|
|
3949
|
-
className="input
|
|
4073
|
+
className="theme-input w-full px-3 py-2 text-sm"
|
|
3950
4074
|
/>
|
|
3951
4075
|
</div>
|
|
3952
4076
|
<div>
|
|
@@ -3956,7 +4080,7 @@ function DebtForm({ features, team, initialData, onSave, onCancel, isEdit }) {
|
|
|
3956
4080
|
value={tags}
|
|
3957
4081
|
onChange={(e) => setTags(e.target.value)}
|
|
3958
4082
|
placeholder="security, performance"
|
|
3959
|
-
className="input
|
|
4083
|
+
className="theme-input w-full px-3 py-2 text-sm"
|
|
3960
4084
|
/>
|
|
3961
4085
|
</div>
|
|
3962
4086
|
</div>
|
|
@@ -3965,7 +4089,7 @@ function DebtForm({ features, team, initialData, onSave, onCancel, isEdit }) {
|
|
|
3965
4089
|
<button type="button" onClick={onCancel} className="px-4 py-2 border border-white/10 text-gray-500 font-mono text-xs hover:text-white transition-all">
|
|
3966
4090
|
CANCEL
|
|
3967
4091
|
</button>
|
|
3968
|
-
<button type="submit" className="btn-
|
|
4092
|
+
<button type="submit" className="theme-btn-primary flex-1 py-2">
|
|
3969
4093
|
{isEdit ? 'UPDATE' : 'ADD DEBT'}
|
|
3970
4094
|
</button>
|
|
3971
4095
|
</div>
|
|
@@ -3980,7 +4104,7 @@ function InfoTab({ projectInfo }) {
|
|
|
3980
4104
|
return (
|
|
3981
4105
|
<div className="space-y-6 animate-fade-in">
|
|
3982
4106
|
{/* Stack */}
|
|
3983
|
-
<div className="
|
|
4107
|
+
<div className="theme-card p-5">
|
|
3984
4108
|
<h3 className="font-mono text-sm text-matrix tracking-wider mb-4"># STACK</h3>
|
|
3985
4109
|
<div className="flex flex-wrap gap-2">
|
|
3986
4110
|
{(projectInfo.stack || []).map((tech, idx) => (
|
|
@@ -3996,7 +4120,7 @@ function InfoTab({ projectInfo }) {
|
|
|
3996
4120
|
|
|
3997
4121
|
{/* Architecture */}
|
|
3998
4122
|
{projectInfo.architecture && (
|
|
3999
|
-
<div className="
|
|
4123
|
+
<div className="theme-card p-5">
|
|
4000
4124
|
<h3 className="font-mono text-sm text-cyber tracking-wider mb-4"># ARCHITECTURE</h3>
|
|
4001
4125
|
<p className="font-mono text-xs text-gray-400 leading-relaxed">{projectInfo.architecture}</p>
|
|
4002
4126
|
</div>
|
|
@@ -4004,7 +4128,7 @@ function InfoTab({ projectInfo }) {
|
|
|
4004
4128
|
|
|
4005
4129
|
{/* Conventions */}
|
|
4006
4130
|
{Object.keys(conventions).length > 0 && (
|
|
4007
|
-
<div className="
|
|
4131
|
+
<div className="theme-card p-5">
|
|
4008
4132
|
<h3 className="font-mono text-sm text-signal tracking-wider mb-4"># CONVENTIONS</h3>
|
|
4009
4133
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
4010
4134
|
{conventions.naming && (
|
|
@@ -4054,7 +4178,7 @@ function InfoTab({ projectInfo }) {
|
|
|
4054
4178
|
|
|
4055
4179
|
{/* Purpose */}
|
|
4056
4180
|
{projectInfo.purpose && (
|
|
4057
|
-
<div className="
|
|
4181
|
+
<div className="theme-card p-5">
|
|
4058
4182
|
<h3 className="font-mono text-sm text-matrix tracking-wider mb-4"># PURPOSE</h3>
|
|
4059
4183
|
<p className="font-mono text-xs text-gray-400 leading-relaxed">{projectInfo.purpose}</p>
|
|
4060
4184
|
</div>
|
|
@@ -4071,6 +4195,267 @@ function InfoTab({ projectInfo }) {
|
|
|
4071
4195
|
);
|
|
4072
4196
|
}
|
|
4073
4197
|
|
|
4198
|
+
// ============ APPEARANCE SECTION ============
|
|
4199
|
+
function AppearanceSection({ language }) {
|
|
4200
|
+
const { theme: currentTheme, setTheme, colorMode, setColorMode, isDark } = useTheme();
|
|
4201
|
+
const [saving, setSaving] = useState(false);
|
|
4202
|
+
const [savingMode, setSavingMode] = useState(false);
|
|
4203
|
+
const toast = useToast();
|
|
4204
|
+
|
|
4205
|
+
const handleThemeChange = async (themeId) => {
|
|
4206
|
+
if (themeId === currentTheme) return;
|
|
4207
|
+
setSaving(true);
|
|
4208
|
+
try {
|
|
4209
|
+
await setTheme(themeId);
|
|
4210
|
+
toast.success(language === 'es' ? 'Tema actualizado' : 'Theme updated');
|
|
4211
|
+
} catch (err) {
|
|
4212
|
+
toast.error(language === 'es' ? 'Error al guardar el tema' : 'Error saving theme');
|
|
4213
|
+
}
|
|
4214
|
+
setSaving(false);
|
|
4215
|
+
};
|
|
4216
|
+
|
|
4217
|
+
const handleColorModeChange = async (mode) => {
|
|
4218
|
+
if (mode === colorMode) return;
|
|
4219
|
+
setSavingMode(true);
|
|
4220
|
+
try {
|
|
4221
|
+
await setColorMode(mode);
|
|
4222
|
+
toast.success(language === 'es' ? 'Modo de color actualizado' : 'Color mode updated');
|
|
4223
|
+
} catch (err) {
|
|
4224
|
+
toast.error(language === 'es' ? 'Error al guardar el modo' : 'Error saving mode');
|
|
4225
|
+
}
|
|
4226
|
+
setSavingMode(false);
|
|
4227
|
+
};
|
|
4228
|
+
|
|
4229
|
+
// Theme preview SVG components
|
|
4230
|
+
const GlassPreview = () => (
|
|
4231
|
+
<svg viewBox="0 0 120 80" className="w-full h-full">
|
|
4232
|
+
{/* Background */}
|
|
4233
|
+
<rect width="120" height="80" fill="#f1f5f9" />
|
|
4234
|
+
{/* Sidebar */}
|
|
4235
|
+
<rect x="0" y="0" width="30" height="80" fill="#ffffff" fillOpacity="0.7" />
|
|
4236
|
+
<rect x="4" y="8" width="22" height="4" rx="2" fill="#10b981" />
|
|
4237
|
+
<rect x="4" y="16" width="22" height="3" rx="1.5" fill="#e2e8f0" />
|
|
4238
|
+
<rect x="4" y="22" width="22" height="3" rx="1.5" fill="#e2e8f0" />
|
|
4239
|
+
<rect x="4" y="28" width="22" height="3" rx="1.5" fill="#e2e8f0" />
|
|
4240
|
+
{/* Cards */}
|
|
4241
|
+
<rect x="36" y="8" width="38" height="28" rx="4" fill="#ffffff" stroke="#e2e8f0" strokeWidth="1" />
|
|
4242
|
+
<rect x="40" y="12" width="20" height="3" rx="1.5" fill="#1e293b" />
|
|
4243
|
+
<rect x="40" y="18" width="30" height="2" rx="1" fill="#94a3b8" />
|
|
4244
|
+
<rect x="40" y="24" width="30" height="4" rx="2" fill="#e2e8f0" />
|
|
4245
|
+
<rect x="40" y="24" width="18" height="4" rx="2" fill="#10b981" />
|
|
4246
|
+
<rect x="80" y="8" width="34" height="28" rx="4" fill="#ffffff" stroke="#e2e8f0" strokeWidth="1" />
|
|
4247
|
+
<rect x="84" y="12" width="16" height="3" rx="1.5" fill="#1e293b" />
|
|
4248
|
+
<rect x="84" y="18" width="26" height="2" rx="1" fill="#94a3b8" />
|
|
4249
|
+
{/* Bottom cards */}
|
|
4250
|
+
<rect x="36" y="44" width="78" height="28" rx="4" fill="#ffffff" stroke="#e2e8f0" strokeWidth="1" />
|
|
4251
|
+
<rect x="40" y="48" width="24" height="3" rx="1.5" fill="#1e293b" />
|
|
4252
|
+
<rect x="40" y="54" width="70" height="2" rx="1" fill="#94a3b8" />
|
|
4253
|
+
<rect x="40" y="60" width="70" height="2" rx="1" fill="#94a3b8" />
|
|
4254
|
+
</svg>
|
|
4255
|
+
);
|
|
4256
|
+
|
|
4257
|
+
const MatrixPreview = () => (
|
|
4258
|
+
<svg viewBox="0 0 120 80" className="w-full h-full">
|
|
4259
|
+
{/* Background with grid */}
|
|
4260
|
+
<rect width="120" height="80" fill="#0a0a0a" />
|
|
4261
|
+
<defs>
|
|
4262
|
+
<pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse">
|
|
4263
|
+
<path d="M 10 0 L 0 0 0 10" fill="none" stroke="#00ff88" strokeWidth="0.2" strokeOpacity="0.1" />
|
|
4264
|
+
</pattern>
|
|
4265
|
+
</defs>
|
|
4266
|
+
<rect width="120" height="80" fill="url(#grid)" />
|
|
4267
|
+
{/* Sidebar */}
|
|
4268
|
+
<rect x="0" y="0" width="30" height="80" fill="#050505" />
|
|
4269
|
+
<line x1="30" y1="0" x2="30" y2="80" stroke="#00ff88" strokeWidth="0.5" strokeOpacity="0.3" />
|
|
4270
|
+
<rect x="0" y="8" width="2" height="6" fill="#00ff88" />
|
|
4271
|
+
<rect x="6" y="9" width="20" height="4" fill="#00ff88" fillOpacity="0.2" />
|
|
4272
|
+
<text x="8" y="12.5" fill="#00ff88" fontSize="3" fontFamily="monospace">> MENU</text>
|
|
4273
|
+
<rect x="6" y="16" width="20" height="3" fill="#333" />
|
|
4274
|
+
<rect x="6" y="22" width="20" height="3" fill="#333" />
|
|
4275
|
+
<rect x="6" y="28" width="20" height="3" fill="#333" />
|
|
4276
|
+
{/* Cards */}
|
|
4277
|
+
<rect x="36" y="8" width="38" height="28" fill="#0d0d0d" stroke="#00ff88" strokeWidth="0.5" strokeOpacity="0.3" />
|
|
4278
|
+
<line x1="36" y1="8" x2="74" y2="8" stroke="#00ff88" strokeWidth="1" strokeOpacity="0.5" />
|
|
4279
|
+
<rect x="40" y="14" width="20" height="3" fill="#e0e0e0" />
|
|
4280
|
+
<rect x="40" y="20" width="30" height="2" fill="#555" />
|
|
4281
|
+
<rect x="40" y="26" width="30" height="4" fill="#1a1a1a" stroke="#333" strokeWidth="0.5" />
|
|
4282
|
+
<rect x="40" y="26" width="18" height="4" fill="#00ff88" />
|
|
4283
|
+
<rect x="80" y="8" width="34" height="28" fill="#0d0d0d" stroke="#00ff88" strokeWidth="0.5" strokeOpacity="0.3" />
|
|
4284
|
+
<line x1="80" y1="8" x2="114" y2="8" stroke="#00ff88" strokeWidth="1" strokeOpacity="0.5" />
|
|
4285
|
+
<rect x="84" y="14" width="16" height="3" fill="#e0e0e0" />
|
|
4286
|
+
<rect x="84" y="20" width="26" height="2" fill="#555" />
|
|
4287
|
+
{/* Bottom card */}
|
|
4288
|
+
<rect x="36" y="44" width="78" height="28" fill="#0d0d0d" stroke="#00ff88" strokeWidth="0.5" strokeOpacity="0.3" />
|
|
4289
|
+
<line x1="36" y1="44" x2="114" y2="44" stroke="#00ff88" strokeWidth="1" strokeOpacity="0.5" />
|
|
4290
|
+
<rect x="40" y="50" width="24" height="3" fill="#e0e0e0" />
|
|
4291
|
+
<rect x="40" y="56" width="70" height="2" fill="#555" />
|
|
4292
|
+
<rect x="40" y="62" width="70" height="2" fill="#555" />
|
|
4293
|
+
</svg>
|
|
4294
|
+
);
|
|
4295
|
+
|
|
4296
|
+
const themeOptions = [
|
|
4297
|
+
{
|
|
4298
|
+
id: 'glass',
|
|
4299
|
+
name: 'Glass',
|
|
4300
|
+
description: language === 'es' ? 'Interfaz moderna y limpia' : 'Modern and clean interface',
|
|
4301
|
+
preview: GlassPreview
|
|
4302
|
+
},
|
|
4303
|
+
{
|
|
4304
|
+
id: 'matrix',
|
|
4305
|
+
name: 'Matrix',
|
|
4306
|
+
description: language === 'es' ? 'Estética cyberpunk terminal' : 'Cyberpunk terminal aesthetic',
|
|
4307
|
+
preview: MatrixPreview
|
|
4308
|
+
}
|
|
4309
|
+
];
|
|
4310
|
+
|
|
4311
|
+
return (
|
|
4312
|
+
<div className="theme-card p-6">
|
|
4313
|
+
<div className="flex items-center gap-3 mb-6">
|
|
4314
|
+
<div className="w-10 h-10 rounded-theme-md flex items-center justify-center" style={{ background: 'var(--accent-primary)', opacity: 0.15 }}>
|
|
4315
|
+
<Palette className="w-5 h-5" style={{ color: 'var(--accent-primary)' }} />
|
|
4316
|
+
</div>
|
|
4317
|
+
<div>
|
|
4318
|
+
<h3 className="font-heading text-lg" style={{ color: 'var(--text-primary)' }}>
|
|
4319
|
+
{language === 'es' ? 'Apariencia' : 'Appearance'}
|
|
4320
|
+
</h3>
|
|
4321
|
+
<p className="text-sm" style={{ color: 'var(--text-muted)' }}>
|
|
4322
|
+
{language === 'es' ? 'Personaliza el aspecto del dashboard' : 'Customize the dashboard appearance'}
|
|
4323
|
+
</p>
|
|
4324
|
+
</div>
|
|
4325
|
+
</div>
|
|
4326
|
+
|
|
4327
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
4328
|
+
{themeOptions.map((themeOpt) => {
|
|
4329
|
+
const isSelected = currentTheme === themeOpt.id;
|
|
4330
|
+
const Preview = themeOpt.preview;
|
|
4331
|
+
|
|
4332
|
+
return (
|
|
4333
|
+
<button
|
|
4334
|
+
key={themeOpt.id}
|
|
4335
|
+
onClick={() => handleThemeChange(themeOpt.id)}
|
|
4336
|
+
disabled={saving}
|
|
4337
|
+
className={`group relative p-4 text-left transition-all rounded-theme-lg ${
|
|
4338
|
+
isSelected
|
|
4339
|
+
? 'ring-2'
|
|
4340
|
+
: 'hover:ring-1'
|
|
4341
|
+
}`}
|
|
4342
|
+
style={{
|
|
4343
|
+
background: 'var(--bg-card)',
|
|
4344
|
+
border: '1px solid var(--border-color)',
|
|
4345
|
+
ringColor: isSelected ? 'var(--accent-primary)' : 'var(--border-color-strong)'
|
|
4346
|
+
}}
|
|
4347
|
+
>
|
|
4348
|
+
{/* Preview */}
|
|
4349
|
+
<div
|
|
4350
|
+
className="w-full aspect-[3/2] mb-4 overflow-hidden rounded-theme-md"
|
|
4351
|
+
style={{ border: '1px solid var(--border-color)' }}
|
|
4352
|
+
>
|
|
4353
|
+
<Preview />
|
|
4354
|
+
</div>
|
|
4355
|
+
|
|
4356
|
+
{/* Info */}
|
|
4357
|
+
<div className="flex items-center justify-between">
|
|
4358
|
+
<div>
|
|
4359
|
+
<h4 className="font-heading font-medium" style={{ color: 'var(--text-primary)' }}>
|
|
4360
|
+
{themeOpt.name}
|
|
4361
|
+
</h4>
|
|
4362
|
+
<p className="text-xs mt-0.5" style={{ color: 'var(--text-muted)' }}>
|
|
4363
|
+
{themeOpt.description}
|
|
4364
|
+
</p>
|
|
4365
|
+
</div>
|
|
4366
|
+
|
|
4367
|
+
{/* Radio indicator */}
|
|
4368
|
+
<div
|
|
4369
|
+
className={`w-5 h-5 rounded-full border-2 flex items-center justify-center transition-all ${
|
|
4370
|
+
isSelected ? 'border-transparent' : ''
|
|
4371
|
+
}`}
|
|
4372
|
+
style={{
|
|
4373
|
+
borderColor: isSelected ? 'var(--accent-primary)' : 'var(--border-color-strong)',
|
|
4374
|
+
background: isSelected ? 'var(--accent-primary)' : 'transparent'
|
|
4375
|
+
}}
|
|
4376
|
+
>
|
|
4377
|
+
{isSelected && (
|
|
4378
|
+
<Check className="w-3 h-3" style={{ color: 'var(--text-inverse)' }} />
|
|
4379
|
+
)}
|
|
4380
|
+
</div>
|
|
4381
|
+
</div>
|
|
4382
|
+
|
|
4383
|
+
{/* Loading overlay */}
|
|
4384
|
+
{saving && isSelected && (
|
|
4385
|
+
<div className="absolute inset-0 flex items-center justify-center rounded-theme-lg" style={{ background: 'rgba(0,0,0,0.5)' }}>
|
|
4386
|
+
<Loader2 className="w-6 h-6 animate-spin" style={{ color: 'var(--accent-primary)' }} />
|
|
4387
|
+
</div>
|
|
4388
|
+
)}
|
|
4389
|
+
</button>
|
|
4390
|
+
);
|
|
4391
|
+
})}
|
|
4392
|
+
</div>
|
|
4393
|
+
|
|
4394
|
+
{/* Color Mode Toggle */}
|
|
4395
|
+
<div className="mt-6 pt-6" style={{ borderTop: '1px solid var(--border-color)' }}>
|
|
4396
|
+
<div className="flex items-center justify-between">
|
|
4397
|
+
<div className="flex items-center gap-3">
|
|
4398
|
+
{isDark ? (
|
|
4399
|
+
<Moon className="w-5 h-5" style={{ color: 'var(--accent-primary)' }} />
|
|
4400
|
+
) : (
|
|
4401
|
+
<Sun className="w-5 h-5" style={{ color: 'var(--accent-secondary)' }} />
|
|
4402
|
+
)}
|
|
4403
|
+
<div>
|
|
4404
|
+
<h4 className="font-heading font-medium" style={{ color: 'var(--text-primary)' }}>
|
|
4405
|
+
{language === 'es' ? 'Modo de color' : 'Color Mode'}
|
|
4406
|
+
</h4>
|
|
4407
|
+
<p className="text-xs" style={{ color: 'var(--text-muted)' }}>
|
|
4408
|
+
{language === 'es'
|
|
4409
|
+
? isDark ? 'Modo oscuro activado' : 'Modo claro activado'
|
|
4410
|
+
: isDark ? 'Dark mode enabled' : 'Light mode enabled'}
|
|
4411
|
+
</p>
|
|
4412
|
+
</div>
|
|
4413
|
+
</div>
|
|
4414
|
+
|
|
4415
|
+
<div className="flex gap-2">
|
|
4416
|
+
<button
|
|
4417
|
+
onClick={() => handleColorModeChange('light')}
|
|
4418
|
+
disabled={savingMode}
|
|
4419
|
+
className={`px-4 py-2 rounded-theme-md transition-all flex items-center gap-2 ${
|
|
4420
|
+
!isDark ? 'ring-2' : ''
|
|
4421
|
+
}`}
|
|
4422
|
+
style={{
|
|
4423
|
+
background: !isDark ? 'var(--accent-primary)' : 'var(--bg-secondary)',
|
|
4424
|
+
color: !isDark ? 'var(--text-inverse)' : 'var(--text-secondary)',
|
|
4425
|
+
ringColor: 'var(--accent-primary)'
|
|
4426
|
+
}}
|
|
4427
|
+
>
|
|
4428
|
+
<Sun className="w-4 h-4" />
|
|
4429
|
+
<span className="text-sm font-medium">{language === 'es' ? 'Claro' : 'Light'}</span>
|
|
4430
|
+
</button>
|
|
4431
|
+
<button
|
|
4432
|
+
onClick={() => handleColorModeChange('dark')}
|
|
4433
|
+
disabled={savingMode}
|
|
4434
|
+
className={`px-4 py-2 rounded-theme-md transition-all flex items-center gap-2 ${
|
|
4435
|
+
isDark ? 'ring-2' : ''
|
|
4436
|
+
}`}
|
|
4437
|
+
style={{
|
|
4438
|
+
background: isDark ? 'var(--accent-primary)' : 'var(--bg-secondary)',
|
|
4439
|
+
color: isDark ? 'var(--text-inverse)' : 'var(--text-secondary)',
|
|
4440
|
+
ringColor: 'var(--accent-primary)'
|
|
4441
|
+
}}
|
|
4442
|
+
>
|
|
4443
|
+
<Moon className="w-4 h-4" />
|
|
4444
|
+
<span className="text-sm font-medium">{language === 'es' ? 'Oscuro' : 'Dark'}</span>
|
|
4445
|
+
</button>
|
|
4446
|
+
</div>
|
|
4447
|
+
</div>
|
|
4448
|
+
</div>
|
|
4449
|
+
|
|
4450
|
+
<p className="text-xs mt-4" style={{ color: 'var(--text-muted)' }}>
|
|
4451
|
+
{language === 'es'
|
|
4452
|
+
? 'El tema y modo se guardan automáticamente en tu perfil'
|
|
4453
|
+
: 'Theme and mode are automatically saved to your profile'}
|
|
4454
|
+
</p>
|
|
4455
|
+
</div>
|
|
4456
|
+
);
|
|
4457
|
+
}
|
|
4458
|
+
|
|
4074
4459
|
// ============ SETTINGS TAB ============
|
|
4075
4460
|
function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfirm, showAlert, t, language, changeLanguage }) {
|
|
4076
4461
|
const [copiedItem, setCopiedItem] = useState(null);
|
|
@@ -4408,6 +4793,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4408
4793
|
// Settings sub-tabs configuration
|
|
4409
4794
|
const settingsTabs = [
|
|
4410
4795
|
{ id: 'profile', label: t('settings.profile'), icon: User, show: !!authState?.user },
|
|
4796
|
+
{ id: 'appearance', label: language === 'es' ? 'APARIENCIA' : 'APPEARANCE', icon: Palette, show: true },
|
|
4411
4797
|
{ id: 'users', label: t('settings.users'), icon: Lock, show: isAdmin },
|
|
4412
4798
|
{ id: 'history', label: t('settings.history'), icon: Clock, show: true },
|
|
4413
4799
|
{ id: 'files', label: t('settings.files'), icon: FileCode, show: true },
|
|
@@ -4446,7 +4832,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4446
4832
|
|
|
4447
4833
|
{/* PROFILE TAB */}
|
|
4448
4834
|
{settingsSubTab === 'profile' && authState?.user && (
|
|
4449
|
-
<div className="
|
|
4835
|
+
<div className="theme-card p-5 space-y-5">
|
|
4450
4836
|
<div className="flex items-center gap-3 mb-2">
|
|
4451
4837
|
<div className="w-12 h-12 border border-cyber/30 bg-cyber/10 flex items-center justify-center font-display text-cyber text-xl">
|
|
4452
4838
|
{authState.user.name?.charAt(0).toUpperCase() || 'U'}
|
|
@@ -4483,7 +4869,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4483
4869
|
type="text"
|
|
4484
4870
|
value={profileData.name}
|
|
4485
4871
|
onChange={(e) => setProfileData(prev => ({ ...prev, name: e.target.value }))}
|
|
4486
|
-
className="w-full input
|
|
4872
|
+
className="w-full theme-input px-3 py-2 text-sm"
|
|
4487
4873
|
placeholder="Your name"
|
|
4488
4874
|
/>
|
|
4489
4875
|
</div>
|
|
@@ -4493,7 +4879,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4493
4879
|
type="email"
|
|
4494
4880
|
value={profileData.email}
|
|
4495
4881
|
onChange={(e) => setProfileData(prev => ({ ...prev, email: e.target.value }))}
|
|
4496
|
-
className="w-full input
|
|
4882
|
+
className="w-full theme-input px-3 py-2 text-sm"
|
|
4497
4883
|
placeholder="your@email.com"
|
|
4498
4884
|
/>
|
|
4499
4885
|
</div>
|
|
@@ -4508,7 +4894,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4508
4894
|
type="password"
|
|
4509
4895
|
value={profileData.currentPassword}
|
|
4510
4896
|
onChange={(e) => setProfileData(prev => ({ ...prev, currentPassword: e.target.value }))}
|
|
4511
|
-
className="w-full input
|
|
4897
|
+
className="w-full theme-input px-3 py-2 text-sm"
|
|
4512
4898
|
placeholder="Required"
|
|
4513
4899
|
/>
|
|
4514
4900
|
</div>
|
|
@@ -4518,7 +4904,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4518
4904
|
type="password"
|
|
4519
4905
|
value={profileData.newPassword}
|
|
4520
4906
|
onChange={(e) => setProfileData(prev => ({ ...prev, newPassword: e.target.value }))}
|
|
4521
|
-
className="w-full input
|
|
4907
|
+
className="w-full theme-input px-3 py-2 text-sm"
|
|
4522
4908
|
placeholder="Min 6 chars"
|
|
4523
4909
|
/>
|
|
4524
4910
|
</div>
|
|
@@ -4528,7 +4914,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4528
4914
|
type="password"
|
|
4529
4915
|
value={profileData.confirmPassword}
|
|
4530
4916
|
onChange={(e) => setProfileData(prev => ({ ...prev, confirmPassword: e.target.value }))}
|
|
4531
|
-
className="w-full input
|
|
4917
|
+
className="w-full theme-input px-3 py-2 text-sm"
|
|
4532
4918
|
placeholder="Repeat"
|
|
4533
4919
|
/>
|
|
4534
4920
|
</div>
|
|
@@ -4538,7 +4924,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4538
4924
|
<button
|
|
4539
4925
|
onClick={updateProfile}
|
|
4540
4926
|
disabled={savingProfile}
|
|
4541
|
-
className="btn-
|
|
4927
|
+
className="theme-btn-primary py-2 px-6 disabled:opacity-50 flex items-center gap-2"
|
|
4542
4928
|
>
|
|
4543
4929
|
{savingProfile ? <Loader2 className="w-4 h-4 animate-spin" /> : <Save className="w-4 h-4" />}
|
|
4544
4930
|
SAVE CHANGES
|
|
@@ -4546,9 +4932,14 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4546
4932
|
</div>
|
|
4547
4933
|
)}
|
|
4548
4934
|
|
|
4935
|
+
{/* APPEARANCE TAB */}
|
|
4936
|
+
{settingsSubTab === 'appearance' && (
|
|
4937
|
+
<AppearanceSection language={language} />
|
|
4938
|
+
)}
|
|
4939
|
+
|
|
4549
4940
|
{/* HISTORY TAB */}
|
|
4550
4941
|
{settingsSubTab === 'history' && (
|
|
4551
|
-
<div className="
|
|
4942
|
+
<div className="theme-card p-5">
|
|
4552
4943
|
<div className="flex items-center justify-between mb-4">
|
|
4553
4944
|
<div>
|
|
4554
4945
|
<h3 className="font-mono text-sm text-white">VERSION HISTORY</h3>
|
|
@@ -4559,7 +4950,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4559
4950
|
<button
|
|
4560
4951
|
onClick={loadVersions}
|
|
4561
4952
|
disabled={loadingVersions}
|
|
4562
|
-
className="btn-
|
|
4953
|
+
className="theme-btn-primary py-2 px-4 text-xs"
|
|
4563
4954
|
>
|
|
4564
4955
|
{loadingVersions ? <Loader2 className="w-4 h-4 animate-spin" /> : <RefreshCw className="w-4 h-4" />}
|
|
4565
4956
|
</button>
|
|
@@ -4571,7 +4962,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4571
4962
|
<Clock className="w-10 h-10 mx-auto mb-3 text-gray-800" />
|
|
4572
4963
|
<p className="font-mono text-xs text-gray-600">NO VERSIONS YET</p>
|
|
4573
4964
|
<p className="font-mono text-[10px] text-gray-700 mt-1">Versions are saved automatically when you save changes</p>
|
|
4574
|
-
<button onClick={loadVersions} className="mt-4 btn-
|
|
4965
|
+
<button onClick={loadVersions} className="mt-4 theme-btn-primary py-2 px-4 text-xs">
|
|
4575
4966
|
LOAD HISTORY
|
|
4576
4967
|
</button>
|
|
4577
4968
|
</div>
|
|
@@ -4636,7 +5027,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4636
5027
|
<button
|
|
4637
5028
|
onClick={() => restoreVersion(selectedVersion.id)}
|
|
4638
5029
|
disabled={restoringVersion}
|
|
4639
|
-
className="btn-
|
|
5030
|
+
className="theme-btn-primary py-2 px-4 text-xs flex items-center gap-2"
|
|
4640
5031
|
>
|
|
4641
5032
|
{restoringVersion ? <Loader2 className="w-4 h-4 animate-spin" /> : <RefreshCw className="w-4 h-4" />}
|
|
4642
5033
|
RESTORE THIS VERSION
|
|
@@ -4778,7 +5169,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4778
5169
|
{settingsSubTab === 'users' && isAdmin && (
|
|
4779
5170
|
<div className="space-y-5">
|
|
4780
5171
|
{/* Auth Settings */}
|
|
4781
|
-
<div className="
|
|
5172
|
+
<div className="theme-card p-5">
|
|
4782
5173
|
<div className="flex items-center justify-between">
|
|
4783
5174
|
<div>
|
|
4784
5175
|
<h3 className="font-mono text-sm text-white">Authentication Required</h3>
|
|
@@ -4794,7 +5185,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4794
5185
|
</div>
|
|
4795
5186
|
|
|
4796
5187
|
{/* Add User */}
|
|
4797
|
-
<div className="
|
|
5188
|
+
<div className="theme-card p-5">
|
|
4798
5189
|
<h3 className="font-mono text-sm text-white mb-4">ADD NEW USER</h3>
|
|
4799
5190
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3 mb-3">
|
|
4800
5191
|
<input
|
|
@@ -4802,26 +5193,26 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4802
5193
|
placeholder="Name *"
|
|
4803
5194
|
value={newUser.name}
|
|
4804
5195
|
onChange={(e) => setNewUser(prev => ({ ...prev, name: e.target.value }))}
|
|
4805
|
-
className="input
|
|
5196
|
+
className="theme-input px-3 py-2 text-sm"
|
|
4806
5197
|
/>
|
|
4807
5198
|
<input
|
|
4808
5199
|
type="email"
|
|
4809
5200
|
placeholder="Email *"
|
|
4810
5201
|
value={newUser.email}
|
|
4811
5202
|
onChange={(e) => setNewUser(prev => ({ ...prev, email: e.target.value }))}
|
|
4812
|
-
className="input
|
|
5203
|
+
className="theme-input px-3 py-2 text-sm"
|
|
4813
5204
|
/>
|
|
4814
5205
|
<input
|
|
4815
5206
|
type="password"
|
|
4816
5207
|
placeholder="Password *"
|
|
4817
5208
|
value={newUser.password}
|
|
4818
5209
|
onChange={(e) => setNewUser(prev => ({ ...prev, password: e.target.value }))}
|
|
4819
|
-
className="input
|
|
5210
|
+
className="theme-input px-3 py-2 text-sm"
|
|
4820
5211
|
/>
|
|
4821
5212
|
<select
|
|
4822
5213
|
value={newUser.role}
|
|
4823
5214
|
onChange={(e) => setNewUser(prev => ({ ...prev, role: e.target.value }))}
|
|
4824
|
-
className="input
|
|
5215
|
+
className="theme-input px-3 py-2 text-sm"
|
|
4825
5216
|
>
|
|
4826
5217
|
<option value="member">Member</option>
|
|
4827
5218
|
<option value="admin">Admin</option>
|
|
@@ -4835,14 +5226,14 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4835
5226
|
<button
|
|
4836
5227
|
onClick={createUser}
|
|
4837
5228
|
disabled={!newUser.name.trim() || !newUser.email.trim() || !newUser.password.trim()}
|
|
4838
|
-
className="btn-
|
|
5229
|
+
className="theme-btn-primary py-2 px-4 disabled:opacity-50"
|
|
4839
5230
|
>
|
|
4840
5231
|
<Plus className="w-4 h-4 inline mr-1" /> CREATE USER
|
|
4841
5232
|
</button>
|
|
4842
5233
|
</div>
|
|
4843
5234
|
|
|
4844
5235
|
{/* Users List */}
|
|
4845
|
-
<div className="
|
|
5236
|
+
<div className="theme-card p-5">
|
|
4846
5237
|
<h3 className="font-mono text-sm text-white mb-4">REGISTERED USERS ({users.length})</h3>
|
|
4847
5238
|
{loadingUsers ? (
|
|
4848
5239
|
<div className="py-8 text-center">
|
|
@@ -4895,9 +5286,9 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4895
5286
|
{settingsSubTab === 'files' && (
|
|
4896
5287
|
<div className="space-y-5">
|
|
4897
5288
|
{/* Deploy */}
|
|
4898
|
-
<div className="
|
|
5289
|
+
<div className="theme-card p-5">
|
|
4899
5290
|
<div className="flex items-center gap-3 mb-4">
|
|
4900
|
-
<div className="led led-
|
|
5291
|
+
<div className="led theme-led theme-led-success" />
|
|
4901
5292
|
<div>
|
|
4902
5293
|
<h3 className="font-mono text-sm text-matrix tracking-wider">DEPLOY CONFIG</h3>
|
|
4903
5294
|
<p className="font-mono text-[10px] text-gray-600">Copy files to project root</p>
|
|
@@ -4920,7 +5311,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4920
5311
|
Also create .cursorrules (for Cursor IDE)
|
|
4921
5312
|
</label>
|
|
4922
5313
|
|
|
4923
|
-
<button onClick={deployToProject} disabled={deploying} className="btn-
|
|
5314
|
+
<button onClick={deployToProject} disabled={deploying} className="theme-btn-primary flex items-center gap-2">
|
|
4924
5315
|
{deploying ? <Loader2 className="w-4 h-4 animate-spin" /> : <Rocket className="w-4 h-4" />}
|
|
4925
5316
|
DEPLOY FILES
|
|
4926
5317
|
</button>
|
|
@@ -4942,7 +5333,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4942
5333
|
</div>
|
|
4943
5334
|
|
|
4944
5335
|
{/* .clinerules */}
|
|
4945
|
-
<div className="
|
|
5336
|
+
<div className="theme-card">
|
|
4946
5337
|
<button
|
|
4947
5338
|
onClick={() => setClineruleExpanded(!clineruleExpanded)}
|
|
4948
5339
|
className="w-full p-4 flex items-center justify-between hover:bg-white/[0.01] transition-colors"
|
|
@@ -4978,7 +5369,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4978
5369
|
<textarea
|
|
4979
5370
|
value={clinerules}
|
|
4980
5371
|
onChange={(e) => setClinerules(e.target.value)}
|
|
4981
|
-
className="w-full h-[400px] input
|
|
5372
|
+
className="w-full h-[400px] theme-input p-4 text-xs resize-none"
|
|
4982
5373
|
spellCheck={false}
|
|
4983
5374
|
/>
|
|
4984
5375
|
) : (
|
|
@@ -4991,7 +5382,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
4991
5382
|
</div>
|
|
4992
5383
|
|
|
4993
5384
|
{/* roadmap.json */}
|
|
4994
|
-
<div className="
|
|
5385
|
+
<div className="theme-card">
|
|
4995
5386
|
<button
|
|
4996
5387
|
onClick={() => setJsonExpanded(!jsonExpanded)}
|
|
4997
5388
|
className="w-full p-4 flex items-center justify-between hover:bg-white/[0.01] transition-colors"
|
|
@@ -5051,7 +5442,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
5051
5442
|
<textarea
|
|
5052
5443
|
value={jsonContent}
|
|
5053
5444
|
onChange={(e) => { setJsonContent(e.target.value); setJsonError(null); }}
|
|
5054
|
-
className="w-full h-[400px] input
|
|
5445
|
+
className="w-full h-[400px] theme-input p-4 text-xs resize-none"
|
|
5055
5446
|
spellCheck={false}
|
|
5056
5447
|
/>
|
|
5057
5448
|
<p className="mt-2 font-mono text-[10px] text-gray-600">
|
|
@@ -5071,7 +5462,7 @@ function SettingsTab({ roadmap, setRoadmap, setHasChanges, authState, showConfir
|
|
|
5071
5462
|
|
|
5072
5463
|
{/* LANGUAGE TAB */}
|
|
5073
5464
|
{settingsSubTab === 'language' && (
|
|
5074
|
-
<div className="
|
|
5465
|
+
<div className="theme-card p-5">
|
|
5075
5466
|
<h3 className="font-mono text-sm text-white mb-2">{t('settings.language')}</h3>
|
|
5076
5467
|
<p className="font-mono text-[10px] text-gray-600 mb-6">
|
|
5077
5468
|
{language === 'es' ? 'Selecciona el idioma de la interfaz' : 'Select interface language'}
|
|
@@ -5188,7 +5579,7 @@ function HelpTab({ t, language }) {
|
|
|
5188
5579
|
{/* Templates */}
|
|
5189
5580
|
{activeSection === 'templates' && (
|
|
5190
5581
|
<div className="space-y-6">
|
|
5191
|
-
<div className="
|
|
5582
|
+
<div className="theme-card p-5">
|
|
5192
5583
|
<h3 className="font-mono text-sm text-matrix tracking-wider mb-4">{t('help.referenceFiles')}</h3>
|
|
5193
5584
|
<p className="font-mono text-[11px] text-gray-500 mb-4">{t('help.sharePaths')}</p>
|
|
5194
5585
|
<div className="space-y-2">
|
|
@@ -5212,7 +5603,7 @@ function HelpTab({ t, language }) {
|
|
|
5212
5603
|
</div>
|
|
5213
5604
|
</div>
|
|
5214
5605
|
|
|
5215
|
-
<div className="
|
|
5606
|
+
<div className="theme-card p-5">
|
|
5216
5607
|
<h3 className="font-mono text-sm text-cyber tracking-wider mb-4">{t('help.aiInstructions')}</h3>
|
|
5217
5608
|
<div className="bg-void-100 border border-white/5 p-4 font-mono text-[11px] text-gray-400 overflow-x-auto">
|
|
5218
5609
|
<pre className="whitespace-pre-wrap">{`Read the example files in:
|
|
@@ -5247,7 +5638,7 @@ Then generate a roadmap.json for my project following this structure:
|
|
|
5247
5638
|
</div>
|
|
5248
5639
|
</div>
|
|
5249
5640
|
|
|
5250
|
-
<div className="
|
|
5641
|
+
<div className="theme-card p-5 border-matrix/20">
|
|
5251
5642
|
<div className="flex items-center justify-between mb-3">
|
|
5252
5643
|
<h4 className="font-mono text-sm text-matrix">{t('help.quickPrompt')}</h4>
|
|
5253
5644
|
<button
|
|
@@ -5276,7 +5667,7 @@ Then help me create/improve the roadmap.json for my project following that forma
|
|
|
5276
5667
|
{/* Commits */}
|
|
5277
5668
|
{activeSection === 'commits' && (
|
|
5278
5669
|
<div className="space-y-6">
|
|
5279
|
-
<div className="
|
|
5670
|
+
<div className="theme-card p-5">
|
|
5280
5671
|
<h3 className="font-mono text-sm text-matrix tracking-wider mb-4">{t('help.commitFormat')}</h3>
|
|
5281
5672
|
<p className="font-mono text-[11px] text-gray-500 mb-4">{t('help.commitFormatDesc')}</p>
|
|
5282
5673
|
<div className="bg-void-100 border border-white/5 p-4 mb-6">
|
|
@@ -5314,7 +5705,7 @@ Then help me create/improve the roadmap.json for my project following that forma
|
|
|
5314
5705
|
</div>
|
|
5315
5706
|
</div>
|
|
5316
5707
|
|
|
5317
|
-
<div className="
|
|
5708
|
+
<div className="theme-card p-5">
|
|
5318
5709
|
<h4 className="font-mono text-xs text-gray-400 mb-4">{t('help.examples')}</h4>
|
|
5319
5710
|
<div className="space-y-3">
|
|
5320
5711
|
{[
|
|
@@ -5330,7 +5721,7 @@ Then help me create/improve the roadmap.json for my project following that forma
|
|
|
5330
5721
|
</div>
|
|
5331
5722
|
</div>
|
|
5332
5723
|
|
|
5333
|
-
<div className="
|
|
5724
|
+
<div className="theme-card p-5 border-cyber/20">
|
|
5334
5725
|
<h4 className="font-mono text-sm text-cyber tracking-wider mb-3">{t('help.syncWithGit')}</h4>
|
|
5335
5726
|
<p className="font-mono text-[11px] text-gray-500 mb-3">{t('help.afterCommits')}</p>
|
|
5336
5727
|
<div className="bg-void-100 border border-white/5 p-3">
|
|
@@ -5346,7 +5737,7 @@ Then help me create/improve the roadmap.json for my project following that forma
|
|
|
5346
5737
|
{/* AI Workflow */}
|
|
5347
5738
|
{activeSection === 'ai' && (
|
|
5348
5739
|
<div className="space-y-6">
|
|
5349
|
-
<div className="
|
|
5740
|
+
<div className="theme-card p-5">
|
|
5350
5741
|
<h3 className="font-mono text-sm text-matrix tracking-wider mb-4">{t('help.recommendedWorkflow')}</h3>
|
|
5351
5742
|
<div className="space-y-2">
|
|
5352
5743
|
{[
|
|
@@ -5363,7 +5754,7 @@ Then help me create/improve the roadmap.json for my project following that forma
|
|
|
5363
5754
|
</div>
|
|
5364
5755
|
|
|
5365
5756
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
5366
|
-
<div className="
|
|
5757
|
+
<div className="theme-card p-5 border-matrix/20">
|
|
5367
5758
|
<h4 className="font-mono text-xs text-matrix tracking-wider mb-4 flex items-center gap-2">
|
|
5368
5759
|
<CheckCircle2 className="w-4 h-4" /> {t('help.aiMustDo')}
|
|
5369
5760
|
</h4>
|
|
@@ -5376,7 +5767,7 @@ Then help me create/improve the roadmap.json for my project following that forma
|
|
|
5376
5767
|
</ul>
|
|
5377
5768
|
</div>
|
|
5378
5769
|
|
|
5379
|
-
<div className="
|
|
5770
|
+
<div className="theme-card p-5 border-alert/20">
|
|
5380
5771
|
<h4 className="font-mono text-xs text-alert tracking-wider mb-4 flex items-center gap-2">
|
|
5381
5772
|
<X className="w-4 h-4" /> {t('help.aiMustNot')}
|
|
5382
5773
|
</h4>
|
|
@@ -5395,7 +5786,7 @@ Then help me create/improve the roadmap.json for my project following that forma
|
|
|
5395
5786
|
{/* Troubleshooting */}
|
|
5396
5787
|
{activeSection === 'troubleshooting' && (
|
|
5397
5788
|
<div className="space-y-4">
|
|
5398
|
-
<div className="
|
|
5789
|
+
<div className="theme-card p-5">
|
|
5399
5790
|
<h3 className="font-mono text-sm text-signal tracking-wider mb-4">{t('help.troubleshooting')}</h3>
|
|
5400
5791
|
<div className="space-y-4">
|
|
5401
5792
|
{[
|
|
@@ -5415,7 +5806,7 @@ Then help me create/improve the roadmap.json for my project following that forma
|
|
|
5415
5806
|
</div>
|
|
5416
5807
|
</div>
|
|
5417
5808
|
|
|
5418
|
-
<div className="
|
|
5809
|
+
<div className="theme-card p-5">
|
|
5419
5810
|
<h4 className="font-mono text-xs text-gray-400 mb-4">{t('help.fileSummary')}</h4>
|
|
5420
5811
|
<div className="overflow-x-auto">
|
|
5421
5812
|
<table className="w-full font-mono text-[11px]">
|
|
@@ -5716,7 +6107,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
5716
6107
|
return (
|
|
5717
6108
|
<div className="space-y-6 animate-fade-in">
|
|
5718
6109
|
{/* Setup Complete Header */}
|
|
5719
|
-
<div className="
|
|
6110
|
+
<div className="theme-card p-8 border-matrix/50 text-center">
|
|
5720
6111
|
<div className="w-20 h-20 mx-auto border-2 border-matrix/50 flex items-center justify-center mb-6 glow-matrix">
|
|
5721
6112
|
<CheckCircle2 className="w-10 h-10 text-matrix" />
|
|
5722
6113
|
</div>
|
|
@@ -5729,7 +6120,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
5729
6120
|
</div>
|
|
5730
6121
|
|
|
5731
6122
|
{/* Roadmap Status Card */}
|
|
5732
|
-
<div className="
|
|
6123
|
+
<div className="theme-card p-5">
|
|
5733
6124
|
<h3 className="font-mono text-sm text-white tracking-wider mb-4">
|
|
5734
6125
|
{t ? t('setup.roadmapStatus') : 'ROADMAP STATUS'}
|
|
5735
6126
|
</h3>
|
|
@@ -5772,7 +6163,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
5772
6163
|
<div className="flex gap-3">
|
|
5773
6164
|
<button
|
|
5774
6165
|
onClick={() => window.location.hash = '#features'}
|
|
5775
|
-
className="btn-
|
|
6166
|
+
className="theme-btn-primary flex-1 py-3 flex items-center justify-center gap-2"
|
|
5776
6167
|
>
|
|
5777
6168
|
<Zap className="w-4 h-4" />
|
|
5778
6169
|
{t ? t('setup.goToDashboard') : 'GO TO DASHBOARD'}
|
|
@@ -5788,7 +6179,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
5788
6179
|
|
|
5789
6180
|
{/* Project Info Summary */}
|
|
5790
6181
|
{roadmap?.project_info && (
|
|
5791
|
-
<div className="
|
|
6182
|
+
<div className="theme-card p-5">
|
|
5792
6183
|
<h3 className="font-mono text-sm text-cyber tracking-wider mb-4"># {roadmap.project_info.name || 'PROJECT'}</h3>
|
|
5793
6184
|
{roadmap.project_info.description && (
|
|
5794
6185
|
<p className="font-mono text-xs text-gray-400 mb-4">{roadmap.project_info.description}</p>
|
|
@@ -5811,7 +6202,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
5811
6202
|
return (
|
|
5812
6203
|
<div className="space-y-6 animate-fade-in">
|
|
5813
6204
|
{/* Header */}
|
|
5814
|
-
<div className="
|
|
6205
|
+
<div className="theme-card p-5 border-matrix/30">
|
|
5815
6206
|
<div className="flex items-center gap-4">
|
|
5816
6207
|
<div className="w-14 h-14 border border-matrix/50 flex items-center justify-center glow-matrix">
|
|
5817
6208
|
<Sparkles className="w-7 h-7 text-matrix" />
|
|
@@ -5864,7 +6255,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
5864
6255
|
{/* Step 1: Choose Connection Mode */}
|
|
5865
6256
|
{activeStep === 1 && (
|
|
5866
6257
|
<div className="space-y-4">
|
|
5867
|
-
<div className="
|
|
6258
|
+
<div className="theme-card p-6">
|
|
5868
6259
|
<h3 className="font-mono text-sm text-matrix tracking-wider mb-4"># {t ? t('setup.chooseConnection') : 'CHOOSE CONNECTION MODE'}</h3>
|
|
5869
6260
|
<p className="font-mono text-xs text-gray-500 mb-6">
|
|
5870
6261
|
{t ? t('setup.chooseConnectionDesc') : 'Choose how to connect with Claude to generate your project configuration.'}
|
|
@@ -5881,7 +6272,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
5881
6272
|
}`}
|
|
5882
6273
|
>
|
|
5883
6274
|
<div className="flex items-center gap-3 mb-3">
|
|
5884
|
-
<div className={`led ${claudeCodeStatus.available ? 'led-
|
|
6275
|
+
<div className={`led ${claudeCodeStatus.available ? 'theme-led theme-led-success' : claudeCodeStatus.checking ? 'theme-led theme-led-warning' : 'theme-led theme-led-danger'}`} />
|
|
5885
6276
|
<span className="font-mono text-sm text-white">CLAUDE CODE</span>
|
|
5886
6277
|
<span className="font-mono text-[9px] px-2 py-0.5 bg-matrix/20 text-matrix border border-matrix/30">
|
|
5887
6278
|
{t ? t('setup.recommended') : 'RECOMMENDED'}
|
|
@@ -5918,7 +6309,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
5918
6309
|
}`}
|
|
5919
6310
|
>
|
|
5920
6311
|
<div className="flex items-center gap-3 mb-3">
|
|
5921
|
-
<div className={`led ${apiKeyStatus.hasKey ? 'led-
|
|
6312
|
+
<div className={`led ${apiKeyStatus.hasKey ? 'theme-led theme-led-success' : 'led-gray'}`} />
|
|
5922
6313
|
<span className="font-mono text-sm text-white">API KEY</span>
|
|
5923
6314
|
</div>
|
|
5924
6315
|
<p className="font-mono text-[11px] text-gray-500 mb-3">
|
|
@@ -5941,7 +6332,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
5941
6332
|
|
|
5942
6333
|
{/* Claude Code selected but not available */}
|
|
5943
6334
|
{generationMode === 'claude-code' && !claudeCodeStatus.checking && !claudeCodeStatus.available && (
|
|
5944
|
-
<div className="
|
|
6335
|
+
<div className="theme-card p-5 border-signal/30">
|
|
5945
6336
|
<h4 className="font-mono text-xs text-signal tracking-wider mb-3">INSTALAR CLAUDE CODE</h4>
|
|
5946
6337
|
<p className="font-mono text-[11px] text-gray-500 mb-4">
|
|
5947
6338
|
Para usar tu suscripción, necesitas tener Claude Code instalado y autenticado.
|
|
@@ -5965,7 +6356,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
5965
6356
|
|
|
5966
6357
|
{/* API mode selected - show key input */}
|
|
5967
6358
|
{generationMode === 'api' && (
|
|
5968
|
-
<div className="
|
|
6359
|
+
<div className="theme-card p-5">
|
|
5969
6360
|
<h4 className="font-mono text-xs text-cyber tracking-wider mb-3">CONFIGURAR API KEY</h4>
|
|
5970
6361
|
<p className="font-mono text-[11px] text-gray-500 mb-4">
|
|
5971
6362
|
Obtén una API key en{' '}
|
|
@@ -5981,7 +6372,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
5981
6372
|
<span className="text-matrix">{apiKeyStatus.maskedKey}</span>
|
|
5982
6373
|
</div>
|
|
5983
6374
|
<div className="flex gap-3">
|
|
5984
|
-
<button onClick={() => setActiveStep(2)} className="btn-
|
|
6375
|
+
<button onClick={() => setActiveStep(2)} className="theme-btn-primary flex-1">
|
|
5985
6376
|
CONTINUE →
|
|
5986
6377
|
</button>
|
|
5987
6378
|
<button
|
|
@@ -5999,12 +6390,12 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
5999
6390
|
placeholder="sk-ant-api03-..."
|
|
6000
6391
|
value={apiKey}
|
|
6001
6392
|
onChange={(e) => setApiKey(e.target.value)}
|
|
6002
|
-
className="input
|
|
6393
|
+
className="theme-input w-full px-4 py-3 text-sm"
|
|
6003
6394
|
/>
|
|
6004
6395
|
<button
|
|
6005
6396
|
onClick={saveApiKey}
|
|
6006
6397
|
disabled={!apiKey.trim()}
|
|
6007
|
-
className="btn-
|
|
6398
|
+
className="theme-btn-primary w-full py-3 disabled:opacity-50"
|
|
6008
6399
|
>
|
|
6009
6400
|
SAVE API KEY
|
|
6010
6401
|
</button>
|
|
@@ -6015,7 +6406,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
6015
6406
|
|
|
6016
6407
|
{/* Continue button for Claude Code mode */}
|
|
6017
6408
|
{generationMode === 'claude-code' && claudeCodeStatus.available && (
|
|
6018
|
-
<button onClick={() => setActiveStep(2)} className="btn-
|
|
6409
|
+
<button onClick={() => setActiveStep(2)} className="theme-btn-primary w-full py-4">
|
|
6019
6410
|
CONTINUE WITH CLAUDE CODE →
|
|
6020
6411
|
</button>
|
|
6021
6412
|
)}
|
|
@@ -6027,7 +6418,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
6027
6418
|
<div className="space-y-4">
|
|
6028
6419
|
{/* Roadmap Validation Status */}
|
|
6029
6420
|
{hasExistingRoadmap && (
|
|
6030
|
-
<div className={`
|
|
6421
|
+
<div className={`theme-card p-5 ${validateRoadmap.valid ? 'border-matrix/50' : validateRoadmap.issues.length > 0 ? 'border-alert/30' : 'border-signal/30'}`}>
|
|
6031
6422
|
<div className="flex items-start gap-4">
|
|
6032
6423
|
<div className={`w-12 h-12 border flex items-center justify-center ${
|
|
6033
6424
|
validateRoadmap.valid ? 'border-matrix/50 bg-matrix/10' :
|
|
@@ -6105,7 +6496,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
6105
6496
|
<div className="flex gap-3">
|
|
6106
6497
|
<button
|
|
6107
6498
|
onClick={() => setActiveStep(4)}
|
|
6108
|
-
className="btn-
|
|
6499
|
+
className="theme-btn-primary px-6 py-2 flex items-center gap-2"
|
|
6109
6500
|
>
|
|
6110
6501
|
<CheckCircle2 className="w-4 h-4" />
|
|
6111
6502
|
CONTINUE TO DASHBOARD
|
|
@@ -6149,10 +6540,13 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
6149
6540
|
)}
|
|
6150
6541
|
|
|
6151
6542
|
{/* Project Scan Results */}
|
|
6152
|
-
<div className="
|
|
6153
|
-
<
|
|
6543
|
+
<div className="theme-card">
|
|
6544
|
+
<div
|
|
6154
6545
|
onClick={() => setScanExpanded(!scanExpanded)}
|
|
6155
|
-
className="w-full p-4 flex items-center justify-between hover:bg-white/[0.01] transition-colors"
|
|
6546
|
+
className="w-full p-4 flex items-center justify-between hover:bg-white/[0.01] transition-colors cursor-pointer"
|
|
6547
|
+
role="button"
|
|
6548
|
+
tabIndex={0}
|
|
6549
|
+
onKeyDown={(e) => e.key === 'Enter' && setScanExpanded(!scanExpanded)}
|
|
6156
6550
|
>
|
|
6157
6551
|
<div className="flex items-center gap-3">
|
|
6158
6552
|
<FolderOpen className="w-4 h-4 text-cyber" />
|
|
@@ -6173,7 +6567,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
6173
6567
|
</button>
|
|
6174
6568
|
<ChevronDown className={`w-4 h-4 text-gray-600 transition-transform ${scanExpanded ? 'rotate-180' : ''}`} />
|
|
6175
6569
|
</div>
|
|
6176
|
-
</
|
|
6570
|
+
</div>
|
|
6177
6571
|
|
|
6178
6572
|
{scanExpanded && projectScan && (
|
|
6179
6573
|
<div className="px-4 pb-4 border-t border-white/5">
|
|
@@ -6229,7 +6623,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
6229
6623
|
</div>
|
|
6230
6624
|
|
|
6231
6625
|
{/* Requirements Input */}
|
|
6232
|
-
<div className="
|
|
6626
|
+
<div className="theme-card p-6">
|
|
6233
6627
|
<h3 className="font-mono text-sm text-matrix tracking-wider mb-2"># REQUIREMENTS</h3>
|
|
6234
6628
|
<p className="font-mono text-[10px] text-gray-500 mb-4">
|
|
6235
6629
|
Describe las características, funcionalidades o mejoras que quieres implementar. Claude analizará tu proyecto y generará un roadmap estructurado.
|
|
@@ -6247,13 +6641,13 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
6247
6641
|
value={requirements}
|
|
6248
6642
|
onChange={(e) => setRequirements(e.target.value)}
|
|
6249
6643
|
rows={10}
|
|
6250
|
-
className="input
|
|
6644
|
+
className="theme-input w-full px-4 py-3 text-sm resize-none mb-4"
|
|
6251
6645
|
/>
|
|
6252
6646
|
|
|
6253
6647
|
<button
|
|
6254
6648
|
onClick={analyzeWithAI}
|
|
6255
6649
|
disabled={generating || !requirements.trim()}
|
|
6256
|
-
className="btn-
|
|
6650
|
+
className="theme-btn-primary w-full py-3 flex items-center justify-center gap-2 disabled:opacity-50"
|
|
6257
6651
|
>
|
|
6258
6652
|
{generating ? (
|
|
6259
6653
|
<>
|
|
@@ -6280,7 +6674,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
6280
6674
|
<div className="space-y-4">
|
|
6281
6675
|
{/* Analysis Summary */}
|
|
6282
6676
|
{generatedResult.analysis && (
|
|
6283
|
-
<div className="
|
|
6677
|
+
<div className="theme-card p-4 border-cyber/30">
|
|
6284
6678
|
<h4 className="font-mono text-xs text-cyber tracking-wider mb-3 flex items-center gap-2">
|
|
6285
6679
|
<Info className="w-4 h-4" /> ANALYSIS SUMMARY
|
|
6286
6680
|
</h4>
|
|
@@ -6312,7 +6706,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
6312
6706
|
</div>
|
|
6313
6707
|
)}
|
|
6314
6708
|
|
|
6315
|
-
<div className="
|
|
6709
|
+
<div className="theme-card p-6">
|
|
6316
6710
|
<h3 className="font-mono text-sm text-matrix tracking-wider mb-4"># GENERATED ROADMAP</h3>
|
|
6317
6711
|
|
|
6318
6712
|
<div className="grid grid-cols-2 gap-4 mb-6">
|
|
@@ -6373,14 +6767,14 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
6373
6767
|
<button onClick={() => setActiveStep(2)} className="px-4 py-2 border border-white/10 text-gray-500 font-mono text-xs hover:text-white transition-all">
|
|
6374
6768
|
← BACK
|
|
6375
6769
|
</button>
|
|
6376
|
-
<button onClick={applyGenerated} className="btn-
|
|
6770
|
+
<button onClick={applyGenerated} className="theme-btn-primary flex-1 py-3">
|
|
6377
6771
|
APPLY & DEPLOY
|
|
6378
6772
|
</button>
|
|
6379
6773
|
</div>
|
|
6380
6774
|
</div>
|
|
6381
6775
|
|
|
6382
6776
|
{/* JSON Preview */}
|
|
6383
|
-
<div className="
|
|
6777
|
+
<div className="theme-card p-4">
|
|
6384
6778
|
<details>
|
|
6385
6779
|
<summary className="font-mono text-xs text-gray-500 cursor-pointer hover:text-white">
|
|
6386
6780
|
VIEW GENERATED JSON
|
|
@@ -6395,7 +6789,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
6395
6789
|
|
|
6396
6790
|
{/* Step 4: Complete */}
|
|
6397
6791
|
{activeStep === 4 && (
|
|
6398
|
-
<div className="
|
|
6792
|
+
<div className="theme-card p-8 text-center">
|
|
6399
6793
|
<div className="w-20 h-20 mx-auto mb-6 border-2 border-matrix flex items-center justify-center glow-matrix">
|
|
6400
6794
|
<CheckCircle2 className="w-10 h-10 text-matrix" />
|
|
6401
6795
|
</div>
|
|
@@ -6407,7 +6801,7 @@ function SetupTab({ roadmap, setRoadmap, setHasChanges, loadRoadmap, t, language
|
|
|
6407
6801
|
<button onClick={() => { setActiveStep(1); setGeneratedResult(null); }} className="px-4 py-2 border border-white/10 text-gray-500 font-mono text-xs hover:text-white transition-all">
|
|
6408
6802
|
START OVER
|
|
6409
6803
|
</button>
|
|
6410
|
-
<button onClick={() => window.location.reload()} className="btn-
|
|
6804
|
+
<button onClick={() => window.location.reload()} className="theme-btn-primary px-6 py-2">
|
|
6411
6805
|
VIEW DASHBOARD
|
|
6412
6806
|
</button>
|
|
6413
6807
|
</div>
|
|
@@ -6445,7 +6839,7 @@ function AddTaskForm({ onAdd, onCancel, team = [], t, language }) {
|
|
|
6445
6839
|
placeholder={t ? t('features.taskName') : "Task name"}
|
|
6446
6840
|
value={name}
|
|
6447
6841
|
onChange={(e) => setName(e.target.value)}
|
|
6448
|
-
className="input
|
|
6842
|
+
className="theme-input w-full px-3 py-2 text-sm"
|
|
6449
6843
|
autoFocus
|
|
6450
6844
|
/>
|
|
6451
6845
|
<textarea
|
|
@@ -6453,10 +6847,10 @@ function AddTaskForm({ onAdd, onCancel, team = [], t, language }) {
|
|
|
6453
6847
|
value={description}
|
|
6454
6848
|
onChange={(e) => setDescription(e.target.value)}
|
|
6455
6849
|
rows={2}
|
|
6456
|
-
className="input
|
|
6850
|
+
className="theme-input w-full px-3 py-2 text-sm resize-none"
|
|
6457
6851
|
/>
|
|
6458
6852
|
<div className="flex items-center gap-3 flex-wrap">
|
|
6459
|
-
<select value={priority} onChange={(e) => setPriority(e.target.value)} className="input
|
|
6853
|
+
<select value={priority} onChange={(e) => setPriority(e.target.value)} className="theme-input px-3 py-2 text-sm">
|
|
6460
6854
|
<option value="high">{t ? t('common.high').toUpperCase() : 'HIGH'}</option>
|
|
6461
6855
|
<option value="medium">{t ? t('common.medium').toUpperCase() : 'MEDIUM'}</option>
|
|
6462
6856
|
<option value="low">{t ? t('common.low').toUpperCase() : 'LOW'}</option>
|
|
@@ -6465,7 +6859,7 @@ function AddTaskForm({ onAdd, onCancel, team = [], t, language }) {
|
|
|
6465
6859
|
<select
|
|
6466
6860
|
value={assignedTo}
|
|
6467
6861
|
onChange={(e) => setAssignedTo(e.target.value)}
|
|
6468
|
-
className="input
|
|
6862
|
+
className="theme-input px-3 py-2 text-sm"
|
|
6469
6863
|
>
|
|
6470
6864
|
<option value="">{t ? t('features.unassigned') : 'Unassigned'}</option>
|
|
6471
6865
|
{team.map(member => (
|
|
@@ -6477,7 +6871,7 @@ function AddTaskForm({ onAdd, onCancel, team = [], t, language }) {
|
|
|
6477
6871
|
<button type="button" onClick={onCancel} className="px-4 py-2 font-mono text-xs text-gray-500 hover:text-white transition-all">
|
|
6478
6872
|
{t ? t('common.cancel').toUpperCase() : 'CANCEL'}
|
|
6479
6873
|
</button>
|
|
6480
|
-
<button type="submit" disabled={!name.trim()} className="btn-
|
|
6874
|
+
<button type="submit" disabled={!name.trim()} className="theme-btn-primary px-4 py-2 disabled:opacity-50">
|
|
6481
6875
|
{t ? t('common.add').toUpperCase() : 'ADD'}
|
|
6482
6876
|
</button>
|
|
6483
6877
|
</div>
|
|
@@ -6518,7 +6912,7 @@ function AddFeatureForm({ onAdd, onClose, team = [], t, language }) {
|
|
|
6518
6912
|
placeholder="e.g. user-auth"
|
|
6519
6913
|
value={id}
|
|
6520
6914
|
onChange={(e) => setId(e.target.value)}
|
|
6521
|
-
className="input
|
|
6915
|
+
className="theme-input w-full px-4 py-2.5 mt-2 text-sm"
|
|
6522
6916
|
/>
|
|
6523
6917
|
</div>
|
|
6524
6918
|
<div>
|
|
@@ -6528,7 +6922,7 @@ function AddFeatureForm({ onAdd, onClose, team = [], t, language }) {
|
|
|
6528
6922
|
placeholder={t ? t('features.featureName') : "Feature name"}
|
|
6529
6923
|
value={name}
|
|
6530
6924
|
onChange={(e) => setName(e.target.value)}
|
|
6531
|
-
className="input
|
|
6925
|
+
className="theme-input w-full px-4 py-2.5 mt-2 text-sm"
|
|
6532
6926
|
autoFocus
|
|
6533
6927
|
/>
|
|
6534
6928
|
</div>
|
|
@@ -6539,12 +6933,12 @@ function AddFeatureForm({ onAdd, onClose, team = [], t, language }) {
|
|
|
6539
6933
|
value={description}
|
|
6540
6934
|
onChange={(e) => setDescription(e.target.value)}
|
|
6541
6935
|
rows={3}
|
|
6542
|
-
className="input
|
|
6936
|
+
className="theme-input w-full px-4 py-2.5 mt-2 text-sm resize-none"
|
|
6543
6937
|
/>
|
|
6544
6938
|
</div>
|
|
6545
6939
|
<div>
|
|
6546
6940
|
<label className="font-mono text-[10px] text-gray-500 tracking-wider">{t ? t('features.priority').toUpperCase() : 'PRIORITY'}</label>
|
|
6547
|
-
<select value={priority} onChange={(e) => setPriority(e.target.value)} className="input
|
|
6941
|
+
<select value={priority} onChange={(e) => setPriority(e.target.value)} className="theme-input w-full px-4 py-2.5 mt-2 text-sm">
|
|
6548
6942
|
<option value="high">{t ? t('common.high').toUpperCase() : 'HIGH'}</option>
|
|
6549
6943
|
<option value="medium">{t ? t('common.medium').toUpperCase() : 'MEDIUM'}</option>
|
|
6550
6944
|
<option value="low">{t ? t('common.low').toUpperCase() : 'LOW'}</option>
|
|
@@ -6556,7 +6950,7 @@ function AddFeatureForm({ onAdd, onClose, team = [], t, language }) {
|
|
|
6556
6950
|
<select
|
|
6557
6951
|
value={assignedTo}
|
|
6558
6952
|
onChange={(e) => setAssignedTo(e.target.value)}
|
|
6559
|
-
className="input
|
|
6953
|
+
className="theme-input w-full px-4 py-2.5 mt-2 text-sm"
|
|
6560
6954
|
>
|
|
6561
6955
|
<option value="">{t ? t('features.unassigned') : 'Unassigned'}</option>
|
|
6562
6956
|
{team.map(member => (
|
|
@@ -6569,7 +6963,7 @@ function AddFeatureForm({ onAdd, onClose, team = [], t, language }) {
|
|
|
6569
6963
|
<button type="button" onClick={onClose} className="flex-1 py-2.5 border border-white/20 text-gray-500 font-mono text-xs hover:bg-white/5 transition-all">
|
|
6570
6964
|
{t ? t('common.cancel').toUpperCase() : 'CANCEL'}
|
|
6571
6965
|
</button>
|
|
6572
|
-
<button type="submit" disabled={!name.trim()} className="btn-
|
|
6966
|
+
<button type="submit" disabled={!name.trim()} className="theme-btn-primary flex-1 py-2.5 disabled:opacity-50">
|
|
6573
6967
|
{language === 'es' ? 'CREAR' : 'CREATE'}
|
|
6574
6968
|
</button>
|
|
6575
6969
|
</div>
|
|
@@ -6581,7 +6975,7 @@ function AddFeatureForm({ onAdd, onClose, team = [], t, language }) {
|
|
|
6581
6975
|
function Modal({ children, onClose }) {
|
|
6582
6976
|
return (
|
|
6583
6977
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 modal-overlay animate-fade-in" onClick={onClose}>
|
|
6584
|
-
<div className="
|
|
6978
|
+
<div className="theme-card p-6 max-w-md w-full" onClick={e => e.stopPropagation()}>
|
|
6585
6979
|
{children}
|
|
6586
6980
|
</div>
|
|
6587
6981
|
</div>
|
|
@@ -6618,7 +7012,7 @@ function ThemeModal({ roadmap, setRoadmap, setHasChanges, onClose }) {
|
|
|
6618
7012
|
|
|
6619
7013
|
return (
|
|
6620
7014
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 modal-overlay animate-fade-in" onClick={onClose}>
|
|
6621
|
-
<div className="
|
|
7015
|
+
<div className="theme-card p-6 max-w-lg w-full" onClick={e => e.stopPropagation()}>
|
|
6622
7016
|
{/* Header */}
|
|
6623
7017
|
<div className="flex items-center justify-between mb-6">
|
|
6624
7018
|
<div className="flex items-center gap-3">
|
|
@@ -6683,14 +7077,14 @@ function ThemeModal({ roadmap, setRoadmap, setHasChanges, onClose }) {
|
|
|
6683
7077
|
}
|
|
6684
7078
|
}}
|
|
6685
7079
|
placeholder="#00ff88"
|
|
6686
|
-
className="input
|
|
7080
|
+
className="theme-input px-3 py-2 text-sm w-32 font-mono"
|
|
6687
7081
|
/>
|
|
6688
7082
|
<input
|
|
6689
7083
|
type="text"
|
|
6690
7084
|
value={currentTheme.name}
|
|
6691
7085
|
onChange={(e) => updateTheme({ ...currentTheme, name: e.target.value })}
|
|
6692
7086
|
placeholder="Theme name"
|
|
6693
|
-
className="input
|
|
7087
|
+
className="theme-input px-3 py-2 text-sm flex-1 font-mono"
|
|
6694
7088
|
/>
|
|
6695
7089
|
</div>
|
|
6696
7090
|
</div>
|
|
@@ -6707,7 +7101,7 @@ function ThemeModal({ roadmap, setRoadmap, setHasChanges, onClose }) {
|
|
|
6707
7101
|
</div>
|
|
6708
7102
|
<div className="flex-1">
|
|
6709
7103
|
<div className="font-mono text-xs text-white">{roadmap?.project_info?.name || 'Project'}</div>
|
|
6710
|
-
<div className="progress
|
|
7104
|
+
<div className="theme-progress mt-2 h-2">
|
|
6711
7105
|
<div
|
|
6712
7106
|
className="h-full transition-all"
|
|
6713
7107
|
style={{
|
|
@@ -6761,9 +7155,9 @@ function LoginScreen({ onLogin, error }) {
|
|
|
6761
7155
|
</div>
|
|
6762
7156
|
|
|
6763
7157
|
{/* Login Form */}
|
|
6764
|
-
<div className="
|
|
7158
|
+
<div className="theme-card p-6">
|
|
6765
7159
|
<div className="flex items-center gap-3 mb-6">
|
|
6766
|
-
<div className="led led-
|
|
7160
|
+
<div className="led theme-led theme-led-warning" />
|
|
6767
7161
|
<span className="font-mono text-xs text-signal tracking-wider">AUTHENTICATION REQUIRED</span>
|
|
6768
7162
|
</div>
|
|
6769
7163
|
|
|
@@ -6777,7 +7171,7 @@ function LoginScreen({ onLogin, error }) {
|
|
|
6777
7171
|
value={email}
|
|
6778
7172
|
onChange={(e) => setEmail(e.target.value)}
|
|
6779
7173
|
placeholder="admin@localhost"
|
|
6780
|
-
className="input
|
|
7174
|
+
className="theme-input w-full pl-10 pr-4 py-3 text-sm"
|
|
6781
7175
|
autoComplete="email"
|
|
6782
7176
|
autoFocus
|
|
6783
7177
|
/>
|
|
@@ -6793,7 +7187,7 @@ function LoginScreen({ onLogin, error }) {
|
|
|
6793
7187
|
value={password}
|
|
6794
7188
|
onChange={(e) => setPassword(e.target.value)}
|
|
6795
7189
|
placeholder="••••••••"
|
|
6796
|
-
className="input
|
|
7190
|
+
className="theme-input w-full pl-10 pr-4 py-3 text-sm"
|
|
6797
7191
|
autoComplete="current-password"
|
|
6798
7192
|
/>
|
|
6799
7193
|
</div>
|
|
@@ -6806,7 +7200,7 @@ function LoginScreen({ onLogin, error }) {
|
|
|
6806
7200
|
</div>
|
|
6807
7201
|
)}
|
|
6808
7202
|
|
|
6809
|
-
<button type="submit" disabled={loading || !email.trim() || !password.trim()} className="btn-
|
|
7203
|
+
<button type="submit" disabled={loading || !email.trim() || !password.trim()} className="theme-btn-primary w-full py-3 flex items-center justify-center gap-2 disabled:opacity-50">
|
|
6810
7204
|
{loading ? (
|
|
6811
7205
|
<>
|
|
6812
7206
|
<Loader2 className="w-4 h-4 animate-spin" />
|
|
@@ -6968,11 +7362,13 @@ Update \`roadmap.json\` with:
|
|
|
6968
7362
|
// Wrap App with providers
|
|
6969
7363
|
function AppWithProviders() {
|
|
6970
7364
|
return (
|
|
6971
|
-
<
|
|
6972
|
-
<
|
|
6973
|
-
<
|
|
6974
|
-
|
|
6975
|
-
|
|
7365
|
+
<ThemeProvider>
|
|
7366
|
+
<ToastProvider>
|
|
7367
|
+
<ActivityProvider>
|
|
7368
|
+
<App />
|
|
7369
|
+
</ActivityProvider>
|
|
7370
|
+
</ToastProvider>
|
|
7371
|
+
</ThemeProvider>
|
|
6976
7372
|
);
|
|
6977
7373
|
}
|
|
6978
7374
|
|