wadi 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/workflows/auto_sync.md +19 -0
- package/.agent/workflows/kivo_pipeline.md +27 -0
- package/.devcontainer/devcontainer.json +27 -0
- package/.github/workflows/kivo-cicd.yml +45 -0
- package/.github/workflows/monorepo-ci.yml +51 -0
- package/.github/workflows/wadi-ci.yml +38 -0
- package/.husky/pre-commit +2 -0
- package/.prettierignore +12 -0
- package/.prettierrc +9 -0
- package/.vscode/settings.json +19 -0
- package/CHANGELOG.md +56 -0
- package/CODE_OF_CONDUCT.md +43 -0
- package/CONTRIBUTING.md +42 -0
- package/DEPLOY_GUIDE.md +143 -0
- package/GO_LIVE_CHECKLIST.md +41 -0
- package/GO_LIVE_COMPLETE_REPORT.md +58 -0
- package/GO_LIVE_VALIDATION.md +39 -0
- package/LEGAL.md +38 -0
- package/MANIFESTO.md +40 -0
- package/MANUAL.md +82 -0
- package/OPS_PLAN.md +90 -0
- package/README.md +126 -0
- package/RELEASE_NOTES.md +51 -0
- package/ROADMAP.md +51 -0
- package/api_listado.txt +2197 -0
- package/apps/api/WADI_PROTOCOL.md +52 -0
- package/apps/api/debug-brain.js +11 -0
- package/apps/api/package.json +21 -0
- package/apps/api/src/core/analisis.js +17 -0
- package/apps/api/src/core/errors.js +31 -0
- package/apps/api/src/core/logger.js +32 -0
- package/apps/api/src/core/prompt-kivo.js +62 -0
- package/apps/api/src/index.js +212 -0
- package/apps/api/src/layers/human_pattern/composeResponse.js +35 -0
- package/apps/api/src/layers/human_pattern/detectPattern.js +39 -0
- package/apps/api/src/layers/human_pattern/heuristics.js +28 -0
- package/apps/api/src/layers/human_pattern/index.js +28 -0
- package/apps/api/src/layers/human_pattern/socialMemory.js +35 -0
- package/apps/api/src/middleware/errorHandler.js +24 -0
- package/apps/api/src/middleware/rateLimiter.js +38 -0
- package/apps/api/src/middleware/requestLogger.js +31 -0
- package/apps/api/src/middleware/upload.js +21 -0
- package/apps/api/src/middleware/validation.js +70 -0
- package/apps/api/src/modules/data.js +25 -0
- package/apps/api/src/modules/marketing.js +54 -0
- package/apps/api/src/modules/projects.js +40 -0
- package/apps/api/src/openai.js +16 -0
- package/apps/api/src/preferences/index.js +20 -0
- package/apps/api/src/register_user.js +22 -0
- package/apps/api/src/routes/kivo.js +58 -0
- package/apps/api/src/routes/monitoring.js +55 -0
- package/apps/api/src/routes.js +656 -0
- package/apps/api/src/supabase.js +17 -0
- package/apps/api/src/tools/index.js +57 -0
- package/apps/api/src/wadi-brain.js +171 -0
- package/apps/api/supabase/migrations/20251218_audit_logs.sql +27 -0
- package/apps/api/supabase/migrations/v2-chat-persistence.sql +83 -0
- package/apps/api/supabase/migrations/v3-cascade-delete.sql +11 -0
- package/apps/api/supabase/migrations/v3-security-fix.sql +90 -0
- package/apps/api/supabase/migrations/v3-storage.sql +36 -0
- package/apps/api/supabase/migrations/v4-gamification.sql +83 -0
- package/apps/api/supabase/migrations/v5-smoke-index.sql +6 -0
- package/apps/api/supabase/migrations/v6-schema-integrity-fix.sql +98 -0
- package/apps/api/supabase/migrations/v7-performance-indexes.sql +5 -0
- package/apps/api/supabase/migrations/v7-security-hardening.sql +132 -0
- package/apps/api/supabase/migrations/v8-security-hardening.sql +79 -0
- package/apps/api/test_human_pattern.js +30 -0
- package/apps/api/test_human_pattern_vague.js +30 -0
- package/apps/api/test_output.txt +76 -0
- package/apps/api/test_vague.js +30 -0
- package/apps/api/test_vague_block.js +30 -0
- package/apps/api/test_wadi.js +31 -0
- package/apps/api/tsconfig.json +13 -0
- package/apps/frontend/.env.local +3 -0
- package/apps/frontend/README.md +73 -0
- package/apps/frontend/eslint.config.js +27 -0
- package/apps/frontend/index.html +49 -0
- package/apps/frontend/package.json +41 -0
- package/apps/frontend/postcss.config.cjs +6 -0
- package/apps/frontend/public/cursors/wadi-neutral.svg +1 -0
- package/apps/frontend/public/cursors/wadi-select.svg +1 -0
- package/apps/frontend/public/icon-192.svg +1 -0
- package/apps/frontend/public/icon-512.svg +1 -0
- package/apps/frontend/public/manifest.webmanifest +23 -0
- package/apps/frontend/public/sw.js +57 -0
- package/apps/frontend/public/vite.svg +1 -0
- package/apps/frontend/public/wadi.svg +5 -0
- package/apps/frontend/src/assets/react.svg +1 -0
- package/apps/frontend/src/components/AuthLoader.tsx +50 -0
- package/apps/frontend/src/components/ChatInput.tsx +272 -0
- package/apps/frontend/src/components/ChatInterface.tsx +202 -0
- package/apps/frontend/src/components/ErrorBoundary.tsx +52 -0
- package/apps/frontend/src/components/InputArea.tsx +201 -0
- package/apps/frontend/src/components/Layout.tsx +73 -0
- package/apps/frontend/src/components/MessageBubble.tsx +66 -0
- package/apps/frontend/src/components/OnboardingModal.tsx +108 -0
- package/apps/frontend/src/components/SettingsModal.tsx +187 -0
- package/apps/frontend/src/components/Sidebar.tsx +171 -0
- package/apps/frontend/src/components/WadiOnboarding.tsx +71 -0
- package/apps/frontend/src/components/auditor/AuditReport.tsx +166 -0
- package/apps/frontend/src/components/auditor/AuditorHeader.tsx +34 -0
- package/apps/frontend/src/components/auditor/ContextPanel.tsx +138 -0
- package/apps/frontend/src/components/auditor/DataDeconstructor.tsx +85 -0
- package/apps/frontend/src/components/auditor/DecisionWall.tsx +65 -0
- package/apps/frontend/src/components/auditor/Dropzone.tsx +137 -0
- package/apps/frontend/src/components/common/Button.tsx +97 -0
- package/apps/frontend/src/components/common/Card.tsx +43 -0
- package/apps/frontend/src/components/common/Input.tsx +73 -0
- package/apps/frontend/src/components/common/Modal.tsx +53 -0
- package/apps/frontend/src/components/ui/Button.tsx +68 -0
- package/apps/frontend/src/components/ui/Card.tsx +86 -0
- package/apps/frontend/src/components/ui/Input.tsx +28 -0
- package/apps/frontend/src/components/ui/LogItem.tsx +64 -0
- package/apps/frontend/src/components/ui/MondayButton.tsx +40 -0
- package/apps/frontend/src/components/ui/MondayCard.tsx +24 -0
- package/apps/frontend/src/components/ui/Scouter.tsx +208 -0
- package/apps/frontend/src/components/ui/TerminalInput.tsx +202 -0
- package/apps/frontend/src/components/ui/Tooltip.tsx +67 -0
- package/apps/frontend/src/config/chatShortcuts.ts +20 -0
- package/apps/frontend/src/config/supabase.ts +6 -0
- package/apps/frontend/src/final_status.txt +3 -0
- package/apps/frontend/src/hooks/useScouter.ts +28 -0
- package/apps/frontend/src/hooks/useStoreHydration.ts +24 -0
- package/apps/frontend/src/improvement_status.txt +5 -0
- package/apps/frontend/src/index.css +88 -0
- package/apps/frontend/src/main.tsx +62 -0
- package/apps/frontend/src/monday_status.txt +7 -0
- package/apps/frontend/src/pages/ChatPage.tsx +201 -0
- package/apps/frontend/src/pages/DashboardPage.tsx +375 -0
- package/apps/frontend/src/pages/IntroWadi.tsx +114 -0
- package/apps/frontend/src/pages/LandingPage.tsx +103 -0
- package/apps/frontend/src/pages/Login.tsx +190 -0
- package/apps/frontend/src/pages/PrivacyPage.tsx +213 -0
- package/apps/frontend/src/pages/ProjectDetail.tsx +80 -0
- package/apps/frontend/src/pages/Projects.tsx +247 -0
- package/apps/frontend/src/pages/TermsPage.tsx +202 -0
- package/apps/frontend/src/router.tsx +83 -0
- package/apps/frontend/src/store/authStore.ts +152 -0
- package/apps/frontend/src/store/chatStore.ts +837 -0
- package/apps/frontend/src/store/documentStore.ts +89 -0
- package/apps/frontend/src/store/projectsStore.ts +111 -0
- package/apps/frontend/src/store/runsStore.ts +98 -0
- package/apps/frontend/src/utils/api.ts +34 -0
- package/apps/frontend/src/vite-env.d.ts +7 -0
- package/apps/frontend/tailwind.config.cjs +32 -0
- package/apps/frontend/tsconfig.app.json +27 -0
- package/apps/frontend/tsconfig.json +7 -0
- package/apps/frontend/tsconfig.node.json +26 -0
- package/apps/frontend/vite.config.ts +25 -0
- package/apps/kivo/.firebase/hosting.d3d3.cache +11 -0
- package/apps/kivo/.firebaserc +5 -0
- package/apps/kivo/BRANDING_GUIDE.md +71 -0
- package/apps/kivo/DEPLOYMENT_READY.md +46 -0
- package/apps/kivo/DEPLOY_URL.md +12 -0
- package/apps/kivo/IMPLEMENTATION_REPORT.md +35 -0
- package/apps/kivo/IMPLEMENTATION_REPORT_FINAL.md +49 -0
- package/apps/kivo/PLAN_MOBILE_2.0.md +44 -0
- package/apps/kivo/PWA_VERIFICATION_GUIDE.md +77 -0
- package/apps/kivo/README.md +28 -0
- package/apps/kivo/REBUILD_REPORT.md +34 -0
- package/apps/kivo/UPGRADE_REPORT.md +35 -0
- package/apps/kivo/android/app/build.gradle +54 -0
- package/apps/kivo/android/app/capacitor.build.gradle +19 -0
- package/apps/kivo/android/app/proguard-rules.pro +21 -0
- package/apps/kivo/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java +26 -0
- package/apps/kivo/android/app/src/main/AndroidManifest.xml +35 -0
- package/apps/kivo/android/app/src/main/java/com/kivo/app/MainActivity.java +5 -0
- package/apps/kivo/android/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
- package/apps/kivo/android/app/src/main/res/drawable/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-hdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-ldpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-mdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-hdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-ldpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-mdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-xhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-xxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-xxxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-xhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-xxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-xxxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-night/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-hdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-ldpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-mdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-hdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-ldpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-mdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-xhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-xxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-xxxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-xhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-xxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-xxxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +34 -0
- package/apps/kivo/android/app/src/main/res/layout/activity_main.xml +12 -0
- package/apps/kivo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +9 -0
- package/apps/kivo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +9 -0
- package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/values/ic_launcher_background.xml +4 -0
- package/apps/kivo/android/app/src/main/res/values/strings.xml +7 -0
- package/apps/kivo/android/app/src/main/res/values/styles.xml +22 -0
- package/apps/kivo/android/app/src/main/res/xml/file_paths.xml +5 -0
- package/apps/kivo/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java +18 -0
- package/apps/kivo/android/build.gradle +29 -0
- package/apps/kivo/android/capacitor.settings.gradle +3 -0
- package/apps/kivo/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/apps/kivo/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/apps/kivo/android/gradle.properties +22 -0
- package/apps/kivo/android/gradlew +252 -0
- package/apps/kivo/android/gradlew.bat +94 -0
- package/apps/kivo/android/settings.gradle +5 -0
- package/apps/kivo/android/variables.gradle +16 -0
- package/apps/kivo/assets/icon.png +0 -0
- package/apps/kivo/assets/splash.png +0 -0
- package/apps/kivo/capacitor.config.json +16 -0
- package/apps/kivo/firebase.json +6 -0
- package/apps/kivo/jest.config.js +4 -0
- package/apps/kivo/package.json +26 -0
- package/apps/kivo/tests_disabled/logic.test.js +34 -0
- package/apps/kivo/www/assets/icon-192.png +0 -0
- package/apps/kivo/www/assets/icon-512.png +0 -0
- package/apps/kivo/www/assets/kivo-icon.png +0 -0
- package/apps/kivo/www/assets/pop.mp3 +0 -0
- package/apps/kivo/www/favicon.ico +0 -0
- package/apps/kivo/www/firebase-config.js +75 -0
- package/apps/kivo/www/index.html +38 -0
- package/apps/kivo/www/manifest.json +29 -0
- package/apps/kivo/www/script.js +72 -0
- package/apps/kivo/www/style.css +82 -0
- package/apps/kivo/www/sw.js +46 -0
- package/apps/kivo-brain-api/.env.example +2 -0
- package/apps/kivo-brain-api/KIVO_BACKEND_SETUP.md +27 -0
- package/apps/kivo-brain-api/controllers/kivoController.js +31 -0
- package/apps/kivo-brain-api/index.js +24 -0
- package/apps/kivo-brain-api/package.json +17 -0
- package/apps/kivo-brain-api/routes/message.js +8 -0
- package/apps/kivo-brain-api/services/openaiService.js +64 -0
- package/apps/tests/wadi-tests.js +155 -0
- package/apps/wadi-brain/docs/README_REMOVE_OPTIONS.md +26 -0
- package/cli/commands/deploy.js +10 -0
- package/cli/commands/docs.js +16 -0
- package/cli/commands/explain.js +29 -0
- package/cli/commands/lint.js +14 -0
- package/cli/index.js +26 -0
- package/cli/package.json +12 -0
- package/docs/CNAME +1 -0
- package/docs/README.md +38 -0
- package/docs/USO.md +30 -0
- package/docs/index.html +46 -0
- package/eslint.config.js +101 -0
- package/final_validation.json +27 -0
- package/frontend_listado.txt +33387 -0
- package/listado.txt +12591 -0
- package/package.json +46 -0
- package/packages/logger/index.js +43 -0
- package/packages/logger/package.json +19 -0
- package/packages/logger/test-logger.js +7 -0
- package/packages_listado.txt +801 -0
- package/pnpm-workspace.yaml +6 -0
- package/reseteador_existencial.ps1 +40 -0
- package/scripts/bump-version.js +26 -0
- package/scripts/env.ps1 +13 -0
- package/scripts/setup_android.ps1 +25 -0
- package/scripts/setup_virtual_env.ps1 +77 -0
- package/scripts/smoke-test.js +84 -0
- package/scripts/trigger_render_deploy.ps1 +3 -0
- package/scripts/validate-release.js +34 -0
- package/temp_check.js +57 -0
- package/tsconfig.json +5 -0
- package/validation_report.json +27 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { useNavigate, useLocation } from "react-router-dom";
|
|
2
|
+
import { useAuthStore } from "../store/authStore";
|
|
3
|
+
import { useChatStore } from "../store/chatStore";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import { MessageSquare, Plus, LogOut, Settings, User } from "lucide-react";
|
|
6
|
+
import { SettingsModal } from "./SettingsModal";
|
|
7
|
+
|
|
8
|
+
interface SidebarProps {
|
|
9
|
+
isOpen?: boolean;
|
|
10
|
+
onClose?: () => void;
|
|
11
|
+
className?: string; // Kept for compat
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function Sidebar({ isOpen, onClose }: SidebarProps) {
|
|
15
|
+
const navigate = useNavigate();
|
|
16
|
+
const location = useLocation();
|
|
17
|
+
const { user, signOut } = useAuthStore();
|
|
18
|
+
const {
|
|
19
|
+
conversations,
|
|
20
|
+
fetchConversations,
|
|
21
|
+
loadConversation,
|
|
22
|
+
resetChat,
|
|
23
|
+
setSidebarOpen,
|
|
24
|
+
deleteConversation,
|
|
25
|
+
fetchCriminalSummary,
|
|
26
|
+
} = useChatStore();
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (user?.id) {
|
|
30
|
+
fetchConversations();
|
|
31
|
+
fetchCriminalSummary();
|
|
32
|
+
}
|
|
33
|
+
}, [user?.id, fetchConversations, fetchCriminalSummary]);
|
|
34
|
+
|
|
35
|
+
const handleNewChat = () => {
|
|
36
|
+
resetChat();
|
|
37
|
+
navigate("/chat");
|
|
38
|
+
onClose?.();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const handleHistoryClick = (id: string) => {
|
|
42
|
+
loadConversation(id);
|
|
43
|
+
navigate(`/chat/${id}`);
|
|
44
|
+
onClose?.();
|
|
45
|
+
setSidebarOpen(false);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const handleDelete = async (e: React.MouseEvent, id: string) => {
|
|
49
|
+
e.stopPropagation();
|
|
50
|
+
if (confirm("¿Borrar chat?")) {
|
|
51
|
+
await deleteConversation(id);
|
|
52
|
+
if (location.pathname.includes(id)) {
|
|
53
|
+
navigate("/chat");
|
|
54
|
+
}
|
|
55
|
+
fetchConversations();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const [showSettings, setShowSettings] = useState(false);
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<aside
|
|
63
|
+
className={`sidebar-drawer ${
|
|
64
|
+
isOpen ? "open" : ""
|
|
65
|
+
} flex flex-col h-full bg-[var(--wadi-surface)] backdrop-blur-xl border-r border-[var(--wadi-border)] w-[280px] shadow-2xl z-50 transition-all duration-300`}
|
|
66
|
+
>
|
|
67
|
+
{/* HEADER */}
|
|
68
|
+
<div className="p-6 flex items-center justify-between">
|
|
69
|
+
<div className="flex items-center gap-3">
|
|
70
|
+
<div className="w-8 h-8 rounded-xl bg-gradient-to-br from-[#8B5CF6] to-[#38bdf8] flex items-center justify-center shadow-md text-white font-bold text-xs">
|
|
71
|
+
W
|
|
72
|
+
</div>
|
|
73
|
+
<h1 className="font-['Outfit'] text-lg font-bold text-[var(--wadi-text)] tracking-tight">
|
|
74
|
+
WADI
|
|
75
|
+
</h1>
|
|
76
|
+
</div>
|
|
77
|
+
<button
|
|
78
|
+
onClick={handleNewChat}
|
|
79
|
+
className="p-2 bg-[var(--wadi-surface)] hover:bg-[var(--wadi-surface-hover)] border border-[var(--wadi-border)] rounded-lg shadow-sm text-[var(--wadi-text-dim)] hover:text-[#8B5CF6] transition-all"
|
|
80
|
+
title="Nuevo Chat"
|
|
81
|
+
>
|
|
82
|
+
<Plus size={18} />
|
|
83
|
+
</button>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
{/* CHAT LIST */}
|
|
87
|
+
<div className="flex-1 overflow-y-auto px-4 space-y-2 py-2">
|
|
88
|
+
<h3 className="text-xs font-semibold text-[var(--wadi-text-dim)] uppercase tracking-wider px-2 mb-2">
|
|
89
|
+
Conversaciones
|
|
90
|
+
</h3>
|
|
91
|
+
{conversations && conversations.length > 0 ? (
|
|
92
|
+
conversations.map((c) => {
|
|
93
|
+
const isActive = location.pathname.includes(c.id);
|
|
94
|
+
return (
|
|
95
|
+
<div
|
|
96
|
+
key={c.id}
|
|
97
|
+
onClick={() => handleHistoryClick(c.id)}
|
|
98
|
+
className={`group flex items-center gap-3 p-3 rounded-xl cursor-pointer transition-all duration-200 ${
|
|
99
|
+
isActive
|
|
100
|
+
? "bg-[var(--wadi-surface-hover)] shadow-sm border border-[var(--wadi-border)]"
|
|
101
|
+
: "hover:bg-[var(--wadi-surface-hover)] hover:shadow-xs border border-transparent"
|
|
102
|
+
}`}
|
|
103
|
+
>
|
|
104
|
+
<MessageSquare
|
|
105
|
+
size={16}
|
|
106
|
+
className={
|
|
107
|
+
isActive
|
|
108
|
+
? "text-[#8B5CF6]"
|
|
109
|
+
: "text-[var(--wadi-text-dim)] group-hover:text-[var(--wadi-text)]"
|
|
110
|
+
}
|
|
111
|
+
/>
|
|
112
|
+
<div className="flex-1 overflow-hidden">
|
|
113
|
+
<p
|
|
114
|
+
className={`text-sm truncate ${
|
|
115
|
+
isActive
|
|
116
|
+
? "font-medium text-[var(--wadi-text)]"
|
|
117
|
+
: "text-[var(--wadi-text-muted)]"
|
|
118
|
+
}`}
|
|
119
|
+
>
|
|
120
|
+
{c.title || "Nueva Conversación"}
|
|
121
|
+
</p>
|
|
122
|
+
</div>
|
|
123
|
+
<button
|
|
124
|
+
onClick={(e) => handleDelete(e, c.id)}
|
|
125
|
+
className="opacity-0 group-hover:opacity-100 text-[var(--wadi-text-dim)] hover:text-red-400 transition-opacity"
|
|
126
|
+
>
|
|
127
|
+
×
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
})
|
|
132
|
+
) : (
|
|
133
|
+
<div className="text-center py-10 text-[var(--wadi-text-dim)] text-sm italic">
|
|
134
|
+
Sin chats recientes.
|
|
135
|
+
</div>
|
|
136
|
+
)}
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
{/* USER FOOTER */}
|
|
140
|
+
<div className="p-4 border-t border-[var(--wadi-border)] bg-[var(--wadi-surface)] backdrop-blur-md">
|
|
141
|
+
<div className="flex items-center gap-3 p-2 rounded-xl hover:bg-[var(--wadi-surface-hover)] transition-colors">
|
|
142
|
+
<div className="w-9 h-9 rounded-full bg-slate-700 flex items-center justify-center text-slate-300">
|
|
143
|
+
<User size={16} />
|
|
144
|
+
</div>
|
|
145
|
+
<div className="flex-1 overflow-hidden">
|
|
146
|
+
<p className="text-sm font-medium text-[var(--wadi-text)] truncate">
|
|
147
|
+
{user?.email?.split("@")[0] || "Usuario"}
|
|
148
|
+
</p>
|
|
149
|
+
<p className="text-[10px] text-[var(--wadi-text-dim)]">En línea</p>
|
|
150
|
+
</div>
|
|
151
|
+
<div className="flex gap-1">
|
|
152
|
+
<button
|
|
153
|
+
onClick={() => setShowSettings(true)}
|
|
154
|
+
className="p-1.5 text-[var(--wadi-text-dim)] hover:text-[#8B5CF6] hover:bg-purple-900/20 rounded-lg transition-colors"
|
|
155
|
+
>
|
|
156
|
+
<Settings size={16} />
|
|
157
|
+
</button>
|
|
158
|
+
<button
|
|
159
|
+
onClick={() => signOut()}
|
|
160
|
+
className="p-1.5 text-[var(--wadi-text-dim)] hover:text-red-500 hover:bg-red-900/20 rounded-lg transition-colors"
|
|
161
|
+
>
|
|
162
|
+
<LogOut size={16} />
|
|
163
|
+
</button>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
{showSettings && <SettingsModal onClose={() => setShowSettings(false)} />}
|
|
169
|
+
</aside>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
|
|
3
|
+
export type WadiMood = "hostile" | "mildly_disappointed" | "training_wheels";
|
|
4
|
+
|
|
5
|
+
interface WadiOnboardingProps {
|
|
6
|
+
mood?: WadiMood;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function WadiOnboarding({
|
|
10
|
+
mood = "hostile",
|
|
11
|
+
}: WadiOnboardingProps) {
|
|
12
|
+
const [step, setStep] = useState(0);
|
|
13
|
+
|
|
14
|
+
const messagesByMood: Record<WadiMood, string[]> = {
|
|
15
|
+
hostile: [
|
|
16
|
+
"*⌛ Cargando paciencia... ERROR 404*",
|
|
17
|
+
"🧠 WADI activo (a regañadientes).",
|
|
18
|
+
"No estoy acá para mimarte,",
|
|
19
|
+
"estoy para que dejes de mentirte.",
|
|
20
|
+
"➡️ Decime qué rompiste hoy,",
|
|
21
|
+
"🌀 o volvé cuando tengas un plan real.",
|
|
22
|
+
],
|
|
23
|
+
mildly_disappointed: [
|
|
24
|
+
"*⌛ WADI está despertando de su siesta funcional...*",
|
|
25
|
+
"🧠 WADI activo.",
|
|
26
|
+
"Esto puede doler menos si cooperás.",
|
|
27
|
+
"Tomemos una decisión antes de que vuelva la confusión.",
|
|
28
|
+
"📌 ¿Por dónde empezamos?",
|
|
29
|
+
"📉 O seguí divagando, pero sin mí.",
|
|
30
|
+
],
|
|
31
|
+
training_wheels: [
|
|
32
|
+
"*⌛ Preparando el espacio para ordenar tus ideas...*",
|
|
33
|
+
"🧠 Hola, soy WADI.",
|
|
34
|
+
"Estoy acá para ayudarte a decidir sin drama.",
|
|
35
|
+
"Podemos ir paso a paso, sin presión.",
|
|
36
|
+
"🗺️ Empezamos cuando quieras.",
|
|
37
|
+
"☕ O tomamos un respiro y seguimos después.",
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const messages = messagesByMood[mood] || messagesByMood.hostile;
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (step < messages.length - 1) {
|
|
45
|
+
const timer = setTimeout(() => setStep(step + 1), 1000);
|
|
46
|
+
return () => clearTimeout(timer);
|
|
47
|
+
}
|
|
48
|
+
}, [step, messages.length]);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className="whitespace-pre-wrap font-mono text-sm leading-relaxed p-4 rounded-xl bg-[var(--color-surface)] border border-[var(--color-border)] w-[90vw] max-w-lg mx-auto mt-10 text-[var(--color-text-main)] flex flex-col">
|
|
52
|
+
{messages.slice(0, step + 1).map((line, index) => (
|
|
53
|
+
<p
|
|
54
|
+
key={index}
|
|
55
|
+
style={{
|
|
56
|
+
marginBottom: "0.25rem",
|
|
57
|
+
color: index === 0 ? "var(--color-text-soft)" : "inherit",
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
{index === 0 ? (
|
|
61
|
+
<span className="animate-pulse">{line}</span>
|
|
62
|
+
) : index === 1 ? (
|
|
63
|
+
<strong>{line}</strong>
|
|
64
|
+
) : (
|
|
65
|
+
line
|
|
66
|
+
)}
|
|
67
|
+
</p>
|
|
68
|
+
))}
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { useParams, useNavigate } from "react-router-dom";
|
|
3
|
+
import { Button } from "../ui/Button";
|
|
4
|
+
import { API_URL } from "../../store/chatStore";
|
|
5
|
+
import { useAuthStore } from "../../store/authStore";
|
|
6
|
+
import { useScouter } from "../../hooks/useScouter";
|
|
7
|
+
import { supabase } from "../../config/supabase";
|
|
8
|
+
|
|
9
|
+
interface Vulnerability {
|
|
10
|
+
level: "HIGH" | "MEDIUM" | "LOW";
|
|
11
|
+
title: string;
|
|
12
|
+
description: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function AuditReport() {
|
|
16
|
+
const { conversationId } = useParams();
|
|
17
|
+
const navigate = useNavigate();
|
|
18
|
+
const { user } = useAuthStore();
|
|
19
|
+
const { playAlertSound } = useScouter();
|
|
20
|
+
|
|
21
|
+
const [vulnerabilities, setVulnerabilities] = useState<Vulnerability[]>([]);
|
|
22
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
23
|
+
const [error, setError] = useState<string | null>(null);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!conversationId || !user) return; // Wait for user
|
|
27
|
+
|
|
28
|
+
const fetchAudit = async () => {
|
|
29
|
+
setIsLoading(true);
|
|
30
|
+
try {
|
|
31
|
+
const { data: sessionData } = await supabase.auth.getSession();
|
|
32
|
+
const token = sessionData.session?.access_token;
|
|
33
|
+
if (!token) throw new Error("NO_AUTH_TOKEN");
|
|
34
|
+
|
|
35
|
+
const res = await fetch(
|
|
36
|
+
`${API_URL}/api/conversations/${conversationId}/audit`,
|
|
37
|
+
{
|
|
38
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (!res.ok) throw new Error("AUDIT_REFUSED_BY_SYSTEM");
|
|
43
|
+
|
|
44
|
+
const jsonData = await res.json();
|
|
45
|
+
setVulnerabilities(jsonData.vulnerabilities || []);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.error(err);
|
|
48
|
+
setError("ERROR DE CONEXIÓN VITAL: No pude sincronizar con tu estado.");
|
|
49
|
+
playAlertSound();
|
|
50
|
+
} finally {
|
|
51
|
+
setIsLoading(false);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
fetchAudit();
|
|
56
|
+
}, [conversationId, user, playAlertSound]);
|
|
57
|
+
|
|
58
|
+
if (isLoading) {
|
|
59
|
+
return (
|
|
60
|
+
<div className="fixed inset-0 z-50 bg-[#0f111a] flex flex-col items-center justify-center p-4">
|
|
61
|
+
<div className="animate-pulse text-[var(--wadi-primary)] font-mono-wadi text-xl tracking-[0.3em] uppercase">
|
|
62
|
+
[SINCRONIZANDO REALIDAD...]
|
|
63
|
+
</div>
|
|
64
|
+
<div className="mt-4 w-64 h-1 bg-[var(--wadi-surface)] overflow-hidden rounded-full">
|
|
65
|
+
<div className="h-full bg-[var(--wadi-primary)] animate-[shimmer_1s_infinite] w-1/3 rounded-full"></div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (error) {
|
|
72
|
+
return (
|
|
73
|
+
<div className="fixed inset-0 z-50 bg-black flex flex-col items-center justify-center p-4">
|
|
74
|
+
<h1 className="text-[var(--wadi-text-muted)] text-xl font-bold font-mono-wadi mb-4">
|
|
75
|
+
DESCONEXIÓN
|
|
76
|
+
</h1>
|
|
77
|
+
<p className="text-white font-mono-wadi text-sm">{error}</p>
|
|
78
|
+
<Button
|
|
79
|
+
onClick={() => navigate(-1)}
|
|
80
|
+
className="mt-8 bg-[var(--wadi-surface)] text-white hover:bg-[var(--wadi-surface)]/80"
|
|
81
|
+
>
|
|
82
|
+
[VOLVER AL REFUGIO]
|
|
83
|
+
</Button>
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const highRiskCount = vulnerabilities.filter(
|
|
89
|
+
(v) => v.level === "HIGH"
|
|
90
|
+
).length;
|
|
91
|
+
const riskPercentage = Math.min(
|
|
92
|
+
100,
|
|
93
|
+
vulnerabilities.length * 20 + highRiskCount * 15
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className="fixed inset-0 z-50 bg-[#0f111a] flex flex-col items-center justify-center p-4 animate-in fade-in duration-500">
|
|
98
|
+
{/* HEADER */}
|
|
99
|
+
<div className="w-full max-w-2xl border-b border-[var(--wadi-primary)]/30 pb-4 mb-8 flex justify-between items-end">
|
|
100
|
+
<div>
|
|
101
|
+
<h1 className="text-3xl font-light font-['Outfit'] text-[var(--wadi-primary)] tracking-widest uppercase">
|
|
102
|
+
Mapa de Distorsión
|
|
103
|
+
</h1>
|
|
104
|
+
<p className="text-[var(--wadi-text-muted)] mt-2 font-mono-wadi text-xs uppercase">
|
|
105
|
+
REF: {conversationId?.split("-")[0]} // ESTADO:{" "}
|
|
106
|
+
{highRiskCount > 0 ? "DISTORSIÓN ACTIVA" : "LÚCIDO"}
|
|
107
|
+
</p>
|
|
108
|
+
</div>
|
|
109
|
+
<div className="text-right">
|
|
110
|
+
<div className="text-4xl font-bold text-white font-['Outfit']">
|
|
111
|
+
{riskPercentage}%
|
|
112
|
+
</div>
|
|
113
|
+
<div className="text-[var(--wadi-text-muted)] text-[10px] uppercase tracking-widest mt-1">
|
|
114
|
+
NIVEL DE AUTOENGAÑO
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
{/* VULNERABILITIES LIST */}
|
|
120
|
+
<div className="w-full max-w-2xl space-y-4 max-h-[60vh] overflow-y-auto pr-2 custom-scrollbar">
|
|
121
|
+
{vulnerabilities.length === 0 ? (
|
|
122
|
+
<div className="text-center py-12 text-[var(--wadi-text-muted)] font-mono-wadi border border-dashed border-[var(--wadi-border)] bg-[var(--wadi-surface)]/20 rounded-sm">
|
|
123
|
+
[TODO CLARO. ESTÁS SIENDO HONESTO CON VOS MISMO.]
|
|
124
|
+
</div>
|
|
125
|
+
) : (
|
|
126
|
+
vulnerabilities.map((vuln, idx) => (
|
|
127
|
+
<div
|
|
128
|
+
key={idx}
|
|
129
|
+
className={`
|
|
130
|
+
border-l-2 p-6 bg-[var(--wadi-surface)]/30 relative overflow-hidden group rounded-r-sm hover:bg-[var(--wadi-surface)]/50 transition-colors
|
|
131
|
+
${vuln.level === "HIGH" ? "border-[var(--wadi-alert)]" : "border-[var(--wadi-primary)]"}
|
|
132
|
+
`}
|
|
133
|
+
>
|
|
134
|
+
<div className="flex justify-between items-start mb-3">
|
|
135
|
+
<span
|
|
136
|
+
className={`
|
|
137
|
+
font-mono-wadi text-[10px] px-2 py-0.5 font-bold tracking-wider uppercase
|
|
138
|
+
${vuln.level === "HIGH" ? "text-[var(--wadi-alert)] bg-[var(--wadi-alert)]/10" : "text-[var(--wadi-primary)] bg-[var(--wadi-primary)]/10"}
|
|
139
|
+
`}
|
|
140
|
+
>
|
|
141
|
+
{vuln.level === "HIGH" ? "ZONA CIEGA CRÍTICA" : "DISTORSIÓN"}
|
|
142
|
+
</span>
|
|
143
|
+
</div>
|
|
144
|
+
<h3 className="font-['Outfit'] text-lg font-medium text-white mb-2">
|
|
145
|
+
{vuln.title}
|
|
146
|
+
</h3>
|
|
147
|
+
<p className="text-[var(--wadi-text-muted)] text-sm font-light leading-relaxed">
|
|
148
|
+
{vuln.description}
|
|
149
|
+
</p>
|
|
150
|
+
</div>
|
|
151
|
+
))
|
|
152
|
+
)}
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
{/* ACTIONS */}
|
|
156
|
+
<div className="w-full max-w-2xl mt-8 flex justify-end gap-4">
|
|
157
|
+
<Button
|
|
158
|
+
className="border border-[var(--wadi-text-muted)] text-[var(--wadi-text-muted)] hover:text-white"
|
|
159
|
+
onClick={() => navigate(-1)}
|
|
160
|
+
>
|
|
161
|
+
[CERRAR MAPA]
|
|
162
|
+
</Button>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export const AuditorHeader: React.FC = () => {
|
|
4
|
+
return (
|
|
5
|
+
<header className="fixed top-0 left-0 w-full h-[60px] bg-[var(--monday-bg)]/90 backdrop-blur-md border-b border-[var(--monday-border)] z-40 flex items-center justify-between px-6">
|
|
6
|
+
<div className="flex items-center gap-3">
|
|
7
|
+
<div className="w-8 h-8 flex items-center justify-center border border-[var(--monday-primary)] rounded-full animate-pulse-soft">
|
|
8
|
+
<div className="w-2 h-2 bg-[var(--monday-primary)] rounded-full"></div>
|
|
9
|
+
</div>
|
|
10
|
+
<div>
|
|
11
|
+
<h1 className="text-xl font-bold tracking-tighter text-white font-mono leading-none">
|
|
12
|
+
MONDAY<span className="text-[var(--monday-primary)]">::OS</span>
|
|
13
|
+
</h1>
|
|
14
|
+
<div className="flex items-center gap-2">
|
|
15
|
+
<span className="w-1.5 h-1.5 bg-green-500 rounded-full animate-blink block"></span>
|
|
16
|
+
<span className="text-[9px] text-[var(--monday-text-dim)] tracking-widest uppercase font-mono">
|
|
17
|
+
ONLINE // V3.0
|
|
18
|
+
</span>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
{/* Decorative Status */}
|
|
24
|
+
<div className="hidden md:flex gap-4 font-mono text-[10px] text-[var(--monday-text-dim)]">
|
|
25
|
+
<span className="border px-2 py-1 border-[var(--monday-border)] rounded">
|
|
26
|
+
MEM: 64TB
|
|
27
|
+
</span>
|
|
28
|
+
<span className="border px-2 py-1 border-[var(--monday-border)] rounded text-[var(--monday-primary)]">
|
|
29
|
+
UPLINK: SECURE
|
|
30
|
+
</span>
|
|
31
|
+
</div>
|
|
32
|
+
</header>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { useChatStore } from "../../store/chatStore";
|
|
2
|
+
import { Target, AlertTriangle } from "lucide-react";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
|
|
5
|
+
export function ContextPanel() {
|
|
6
|
+
const { messages, activeFocus, rank, points } = useChatStore();
|
|
7
|
+
|
|
8
|
+
// Extract diagnoses from messages
|
|
9
|
+
const diagnoses = useMemo(() => {
|
|
10
|
+
return messages
|
|
11
|
+
.filter((m) => m.diagnosis)
|
|
12
|
+
.map((m) => ({
|
|
13
|
+
id: m.id,
|
|
14
|
+
parsed: m.diagnosis?.replace(/_/g, " "),
|
|
15
|
+
timestamp: m.created_at,
|
|
16
|
+
}))
|
|
17
|
+
.reverse(); // Newest first
|
|
18
|
+
}, [messages]);
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
// Future V2: Extract implied commitments
|
|
22
|
+
const commitments = useMemo(() => {
|
|
23
|
+
// ...
|
|
24
|
+
}, [activeFocus]);
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="flex flex-col h-full p-6 gap-8 bg-zinc-50/50">
|
|
29
|
+
{/* HEADER */}
|
|
30
|
+
<div className="flex flex-col gap-1">
|
|
31
|
+
<h2 className="text-sm font-bold tracking-tight text-zinc-900 uppercase">
|
|
32
|
+
Tablero de Control
|
|
33
|
+
</h2>
|
|
34
|
+
<span className="text-[10px] text-zinc-500 font-medium tracking-wide">
|
|
35
|
+
FACT SHEET & METRICS
|
|
36
|
+
</span>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
{/* 1. ACTIVE FOCUS CARD */}
|
|
40
|
+
<div
|
|
41
|
+
className={`p-4 bg-white rounded-2xl shadow-sm border relative overflow-hidden group transition-colors duration-500 ${activeFocus ? "border-amber-400/30" : "border-zinc-200/60"}`}
|
|
42
|
+
>
|
|
43
|
+
<div
|
|
44
|
+
className={`absolute top-0 left-0 w-1 h-full transition-colors duration-500 ${activeFocus ? "bg-amber-500" : "bg-zinc-300"}`}
|
|
45
|
+
></div>
|
|
46
|
+
<div className="flex items-start justify-between mb-2">
|
|
47
|
+
<div className="flex items-center gap-2 text-zinc-600">
|
|
48
|
+
<Target
|
|
49
|
+
size={14}
|
|
50
|
+
className={
|
|
51
|
+
activeFocus ? "text-amber-600 animate-pulse" : "text-zinc-400"
|
|
52
|
+
}
|
|
53
|
+
/>
|
|
54
|
+
<span
|
|
55
|
+
className={`text-[10px] font-bold uppercase tracking-wider ${activeFocus ? "text-amber-700" : "text-zinc-400"}`}
|
|
56
|
+
>
|
|
57
|
+
{activeFocus ? "Foco Pendiente" : "Sin Objetivo"}
|
|
58
|
+
</span>
|
|
59
|
+
</div>
|
|
60
|
+
{activeFocus && (
|
|
61
|
+
<span className="flex h-2 w-2 rounded-full bg-amber-500 animate-[pulse_3s_infinite]" />
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
<p className="text-sm font-medium text-zinc-800 leading-snug">
|
|
65
|
+
{activeFocus || "El sistema espera una declaración de intención."}
|
|
66
|
+
</p>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
{/* 2. DIAGNOSIS LOG */}
|
|
70
|
+
<div className="flex flex-col gap-3 flex-1 min-h-0">
|
|
71
|
+
<div className="flex items-center justify-between text-zinc-500">
|
|
72
|
+
<div className="flex items-center gap-2">
|
|
73
|
+
<AlertTriangle size={14} />
|
|
74
|
+
<span className="text-[10px] font-bold uppercase tracking-wider">
|
|
75
|
+
Historial Clínico
|
|
76
|
+
</span>
|
|
77
|
+
</div>
|
|
78
|
+
<span className="text-[10px] bg-zinc-200 px-1.5 py-0.5 rounded-full">
|
|
79
|
+
{diagnoses.length}
|
|
80
|
+
</span>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<div className="flex flex-col gap-2 overflow-y-auto pr-2 -mr-2">
|
|
84
|
+
{diagnoses.length === 0 ? (
|
|
85
|
+
<div className="p-4 border border-dashed border-zinc-200 rounded-xl text-center">
|
|
86
|
+
<span className="text-xs text-zinc-400">
|
|
87
|
+
Sin anomalías detectadas.
|
|
88
|
+
</span>
|
|
89
|
+
</div>
|
|
90
|
+
) : (
|
|
91
|
+
diagnoses.map((diag) => (
|
|
92
|
+
<div
|
|
93
|
+
key={diag.id}
|
|
94
|
+
className="p-3 bg-red-50/50 border border-red-100 rounded-xl flex flex-col gap-1"
|
|
95
|
+
>
|
|
96
|
+
<div className="flex items-center justify-between">
|
|
97
|
+
<span className="text-[10px] font-bold text-red-700 uppercase">
|
|
98
|
+
{diag.parsed}
|
|
99
|
+
</span>
|
|
100
|
+
<span className="text-[9px] text-red-400 opacity-60">
|
|
101
|
+
{diag.timestamp
|
|
102
|
+
? new Date(diag.timestamp).toLocaleTimeString([], {
|
|
103
|
+
hour: "2-digit",
|
|
104
|
+
minute: "2-digit",
|
|
105
|
+
})
|
|
106
|
+
: ""}
|
|
107
|
+
</span>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
))
|
|
111
|
+
)}
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
{/* 3. METRICS FOOTER */}
|
|
116
|
+
<div className="mt-auto pt-6 border-t border-zinc-200 flex flex-col gap-4">
|
|
117
|
+
<div className="flex items-center justify-between">
|
|
118
|
+
<span className="text-xs text-zinc-500 font-medium">Eficiencia</span>
|
|
119
|
+
<span className="text-sm font-bold font-mono text-zinc-900">
|
|
120
|
+
{points} PTS
|
|
121
|
+
</span>
|
|
122
|
+
</div>
|
|
123
|
+
<div className="w-full bg-zinc-200 h-1.5 rounded-full overflow-hidden">
|
|
124
|
+
{/* Visual progress bar based on points (clamped 0-1000) */}
|
|
125
|
+
<div
|
|
126
|
+
className="h-full bg-zinc-800 transition-all duration-500"
|
|
127
|
+
style={{
|
|
128
|
+
width: `${Math.min(Math.max((points / 1000) * 100, 5), 100)}%`,
|
|
129
|
+
}}
|
|
130
|
+
/>
|
|
131
|
+
</div>
|
|
132
|
+
<div className="flex items-center gap-2 text-[10px] text-zinc-400 uppercase tracking-widest justify-center">
|
|
133
|
+
<span>Rango: {rank.replace(/_/g, " ")}</span>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { MondayCard } from "../ui/MondayCard";
|
|
3
|
+
|
|
4
|
+
interface DeconstructItem {
|
|
5
|
+
item: string;
|
|
6
|
+
category: "CRÍTICO" | "RUIDO" | "VULNERABILIDAD";
|
|
7
|
+
verdict: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface DataDeconstructorProps {
|
|
11
|
+
items: DeconstructItem[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const DataDeconstructor: React.FC<DataDeconstructorProps> = ({
|
|
15
|
+
items,
|
|
16
|
+
}) => {
|
|
17
|
+
const getColor = (cat: string) => {
|
|
18
|
+
switch (cat) {
|
|
19
|
+
case "CRÍTICO":
|
|
20
|
+
return "text-[var(--wadi-primary)] border-[var(--wadi-primary)]";
|
|
21
|
+
case "VULNERABILIDAD":
|
|
22
|
+
return "text-[var(--wadi-red)] border-[var(--wadi-red)]";
|
|
23
|
+
default:
|
|
24
|
+
return "text-[var(--wadi-gray)] border-[var(--wadi-gray)]";
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<MondayCard
|
|
30
|
+
title="DECONSTRUCTOR_V3.0"
|
|
31
|
+
className="w-full my-4 overflow-hidden"
|
|
32
|
+
>
|
|
33
|
+
<div className="overflow-x-auto">
|
|
34
|
+
<table className="w-full text-left border-collapse font-mono text-xs">
|
|
35
|
+
<thead>
|
|
36
|
+
<tr className="border-b border-[var(--wadi-border)] text-[var(--wadi-text-dim)]">
|
|
37
|
+
<th className="p-3 uppercase">Input Data</th>
|
|
38
|
+
<th className="p-3 uppercase">Class</th>
|
|
39
|
+
<th className="p-3 uppercase">System Verdict</th>
|
|
40
|
+
</tr>
|
|
41
|
+
</thead>
|
|
42
|
+
<tbody>
|
|
43
|
+
{items.map((row, idx) => (
|
|
44
|
+
<tr
|
|
45
|
+
key={idx}
|
|
46
|
+
className="border-b border-[rgba(255,255,255,0.05)] hover:bg-[rgba(255,255,255,0.02)]"
|
|
47
|
+
>
|
|
48
|
+
<td className="p-3 text-[var(--wadi-text)]">{row.item}</td>
|
|
49
|
+
<td className="p-3">
|
|
50
|
+
<span
|
|
51
|
+
className={`border px-2 py-0.5 text-[10px] font-bold ${getColor(row.category)}`}
|
|
52
|
+
>
|
|
53
|
+
{row.category}
|
|
54
|
+
</span>
|
|
55
|
+
</td>
|
|
56
|
+
<td className="p-3 text-[var(--wadi-text-dim)] italic">
|
|
57
|
+
"{row.verdict}"
|
|
58
|
+
</td>
|
|
59
|
+
</tr>
|
|
60
|
+
))}
|
|
61
|
+
</tbody>
|
|
62
|
+
</table>
|
|
63
|
+
</div>
|
|
64
|
+
<div className="p-2 border-t border-[var(--wadi-border)] text-[10px] text-[var(--wadi-text-dim)] flex justify-between">
|
|
65
|
+
<span>TOTAL ITEMS: {items.length}</span>
|
|
66
|
+
<button
|
|
67
|
+
onClick={() => {
|
|
68
|
+
const text = items
|
|
69
|
+
.map((i) => `[${i.category}] ${i.item} -> ${i.verdict}`)
|
|
70
|
+
.join("\n");
|
|
71
|
+
const blob = new Blob([text], { type: "text/plain" });
|
|
72
|
+
const url = URL.createObjectURL(blob);
|
|
73
|
+
const a = document.createElement("a");
|
|
74
|
+
a.href = url;
|
|
75
|
+
a.download = `WADI_PLAN_${Date.now()}.txt`;
|
|
76
|
+
a.click();
|
|
77
|
+
}}
|
|
78
|
+
className="hover:text-[var(--wadi-primary)] hover:underline cursor-pointer uppercase font-bold"
|
|
79
|
+
>
|
|
80
|
+
[EXPORT_PLAN]
|
|
81
|
+
</button>
|
|
82
|
+
</div>
|
|
83
|
+
</MondayCard>
|
|
84
|
+
);
|
|
85
|
+
};
|