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,247 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import type { FormEvent } from "react";
|
|
3
|
+
import { Link } from "react-router-dom";
|
|
4
|
+
import { useProjectsStore } from "../store/projectsStore";
|
|
5
|
+
import { Layout } from "../components/Layout";
|
|
6
|
+
import { Button } from "../components/common/Button";
|
|
7
|
+
import { Input } from "../components/common/Input";
|
|
8
|
+
import { Modal } from "../components/common/Modal";
|
|
9
|
+
|
|
10
|
+
// Define interface for project with extra fields if not in store type
|
|
11
|
+
interface ProjectWithMetrics {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
created_at: string;
|
|
16
|
+
noise_count?: number;
|
|
17
|
+
total_items_audited?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default function Projects() {
|
|
21
|
+
const { projects, fetchProjects, createProject, loading } =
|
|
22
|
+
useProjectsStore();
|
|
23
|
+
// ...
|
|
24
|
+
const [name, setName] = useState("");
|
|
25
|
+
const [desc, setDesc] = useState("");
|
|
26
|
+
const [isCreating, setIsCreating] = useState(false);
|
|
27
|
+
const [nameError, setNameError] = useState("");
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
fetchProjects();
|
|
31
|
+
}, [fetchProjects]);
|
|
32
|
+
|
|
33
|
+
const handleCreate = async (e: FormEvent) => {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
if (!name.trim()) {
|
|
36
|
+
setNameError("El nombre es obligatorio");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
setNameError("");
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
await createProject(name, desc);
|
|
44
|
+
setName("");
|
|
45
|
+
setDesc("");
|
|
46
|
+
setIsCreating(false);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
console.error("Error creating project:", err);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const openCreateModal = () => {
|
|
53
|
+
setNameError("");
|
|
54
|
+
setIsCreating(true);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<Layout>
|
|
59
|
+
<div className="w-full max-w-5xl mx-auto p-8 min-h-full">
|
|
60
|
+
{/* Header simple */}
|
|
61
|
+
<header className="flex justify-end mb-8"></header>
|
|
62
|
+
|
|
63
|
+
{/* Hero Section */}
|
|
64
|
+
<div className="text-center mb-12">
|
|
65
|
+
<div className="w-16 h-16 bg-gradient-to-tr from-[#38bdf8] to-[#84cc16] rounded-full mx-auto mb-6 flex items-center justify-center text-3xl shadow-[0_0_30px_rgba(56,189,248,0.3)] animate-in zoom-in duration-500">
|
|
66
|
+
🚀
|
|
67
|
+
</div>
|
|
68
|
+
<h1 className="text-3xl font-bold mb-2 bg-clip-text text-transparent bg-gradient-to-r from-slate-800 to-slate-500">
|
|
69
|
+
Punto de Control
|
|
70
|
+
</h1>
|
|
71
|
+
<p className="text-slate-500 text-lg">¿En qué trabajamos hoy?</p>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
{/* Action Grid */}
|
|
75
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-12">
|
|
76
|
+
{/* Card 1: New Project */}
|
|
77
|
+
<button
|
|
78
|
+
onClick={openCreateModal}
|
|
79
|
+
className="flex flex-col gap-4 items-start text-left p-8 rounded-3xl border border-purple-100 bg-white/60 backdrop-blur-md shadow-sm transition-all hover:scale-[1.02] hover:shadow-lg hover:border-purple-200 group"
|
|
80
|
+
>
|
|
81
|
+
<div className="text-3xl p-3 bg-purple-50 rounded-2xl group-hover:bg-purple-100 transition-colors">
|
|
82
|
+
✨
|
|
83
|
+
</div>
|
|
84
|
+
<div>
|
|
85
|
+
<span className="block text-xl font-bold text-slate-800 mb-2">
|
|
86
|
+
Crear nuevo proyecto
|
|
87
|
+
</span>
|
|
88
|
+
<span className="text-sm text-slate-500 leading-relaxed">
|
|
89
|
+
Inicia una nueva sesión de trabajo desde cero.
|
|
90
|
+
</span>
|
|
91
|
+
</div>
|
|
92
|
+
</button>
|
|
93
|
+
|
|
94
|
+
{/* Card 2: Explore */}
|
|
95
|
+
<div className="flex flex-col gap-4 items-start text-left p-8 rounded-3xl border border-slate-100 bg-white/40 backdrop-blur-sm opacity-80">
|
|
96
|
+
<div className="text-3xl p-3 bg-slate-50 rounded-2xl grayscale">
|
|
97
|
+
📚
|
|
98
|
+
</div>
|
|
99
|
+
<div>
|
|
100
|
+
<span className="block text-xl font-bold text-slate-700 mb-2">
|
|
101
|
+
Explorar recursos
|
|
102
|
+
</span>
|
|
103
|
+
<span className="text-sm text-slate-500 leading-relaxed">
|
|
104
|
+
Documentación y guías (Próximamente).
|
|
105
|
+
</span>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
{/* Recent Projects List */}
|
|
111
|
+
<div>
|
|
112
|
+
<h3 className="text-xs font-bold text-slate-400 mb-4 uppercase tracking-widest pl-2">
|
|
113
|
+
Recientes
|
|
114
|
+
</h3>
|
|
115
|
+
|
|
116
|
+
{loading && (
|
|
117
|
+
<p className="text-slate-400 pl-2 animate-pulse">
|
|
118
|
+
Sincronizando...
|
|
119
|
+
</p>
|
|
120
|
+
)}
|
|
121
|
+
|
|
122
|
+
<div className="flex flex-col gap-4">
|
|
123
|
+
{projects.map((p) => (
|
|
124
|
+
<Link
|
|
125
|
+
key={p.id}
|
|
126
|
+
to={`/projects/${p.id}`}
|
|
127
|
+
className="no-underline"
|
|
128
|
+
>
|
|
129
|
+
<div className="group p-6 flex items-center justify-between rounded-3xl border border-white/50 bg-white/70 backdrop-blur-md shadow-sm transition-all hover:shadow-md hover:bg-white hover:scale-[1.01]">
|
|
130
|
+
<div className="flex items-center gap-4">
|
|
131
|
+
<div className="w-12 h-12 bg-slate-100 rounded-2xl flex items-center justify-center text-xl group-hover:bg-purple-50 group-hover:text-purple-500 transition-colors">
|
|
132
|
+
📄
|
|
133
|
+
</div>
|
|
134
|
+
<div>
|
|
135
|
+
<div className="font-semibold text-base text-slate-800 mb-1 group-hover:text-purple-600 transition-colors">
|
|
136
|
+
{p.name}
|
|
137
|
+
</div>
|
|
138
|
+
{p.description && (
|
|
139
|
+
<div className="text-sm text-slate-500">
|
|
140
|
+
{p.description}
|
|
141
|
+
</div>
|
|
142
|
+
)}
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<div className="flex items-center gap-3">
|
|
147
|
+
<span className="text-xs text-slate-400 bg-slate-50 px-3 py-1 rounded-full">
|
|
148
|
+
{new Date(p.created_at).toLocaleDateString()}
|
|
149
|
+
</span>
|
|
150
|
+
|
|
151
|
+
{/* Smoke Index Calculation */}
|
|
152
|
+
{(() => {
|
|
153
|
+
const pWithMetrics = p as unknown as ProjectWithMetrics;
|
|
154
|
+
const noise = pWithMetrics.noise_count || 0;
|
|
155
|
+
const total = pWithMetrics.total_items_audited || 0;
|
|
156
|
+
const percentage =
|
|
157
|
+
total > 0 ? Math.round((noise / total) * 100) : 0;
|
|
158
|
+
|
|
159
|
+
let colorClass = "text-purple-500 bg-purple-50";
|
|
160
|
+
|
|
161
|
+
if (percentage > 50) {
|
|
162
|
+
colorClass = "text-red-500 bg-red-50 animate-pulse";
|
|
163
|
+
} else if (percentage > 20) {
|
|
164
|
+
colorClass = "text-amber-500 bg-amber-50";
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<span
|
|
169
|
+
className={`text-[10px] font-mono px-3 py-1 rounded-full ${colorClass}`}
|
|
170
|
+
>
|
|
171
|
+
HUMO: {percentage}%
|
|
172
|
+
</span>
|
|
173
|
+
);
|
|
174
|
+
})()}
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</Link>
|
|
178
|
+
))}
|
|
179
|
+
|
|
180
|
+
{projects.length === 0 && !loading && (
|
|
181
|
+
<div className="text-center py-16 px-8 bg-white/30 rounded-3xl border border-dashed border-slate-200 flex flex-col items-center gap-4">
|
|
182
|
+
<div className="text-4xl">🌱</div>
|
|
183
|
+
<h3 className="text-lg font-medium text-slate-700">
|
|
184
|
+
Tu espacio está limpio
|
|
185
|
+
</h3>
|
|
186
|
+
<p className="text-slate-500 max-w-xs mx-auto">
|
|
187
|
+
Aún no tienes proyectos. ¡Es hora de empezar algo nuevo!
|
|
188
|
+
</p>
|
|
189
|
+
<Button
|
|
190
|
+
onClick={openCreateModal}
|
|
191
|
+
variant="primary"
|
|
192
|
+
className="mt-4 shadow-lg shadow-purple-200"
|
|
193
|
+
>
|
|
194
|
+
🚀 Creá tu primer proyecto
|
|
195
|
+
</Button>
|
|
196
|
+
</div>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
{/* Modal for Creating Project */}
|
|
202
|
+
<Modal
|
|
203
|
+
isOpen={isCreating}
|
|
204
|
+
onClose={() => setIsCreating(false)}
|
|
205
|
+
title="Nuevo Proyecto"
|
|
206
|
+
>
|
|
207
|
+
<form onSubmit={handleCreate} className="flex flex-col gap-4">
|
|
208
|
+
<Input
|
|
209
|
+
label="Nombre del proyecto"
|
|
210
|
+
placeholder="Ej. Análisis de datos"
|
|
211
|
+
value={name}
|
|
212
|
+
onChange={(e) => {
|
|
213
|
+
setName(e.target.value);
|
|
214
|
+
if (e.target.value.trim()) setNameError("");
|
|
215
|
+
}}
|
|
216
|
+
error={nameError}
|
|
217
|
+
autoFocus
|
|
218
|
+
/>
|
|
219
|
+
<Input
|
|
220
|
+
label="Descripción (opcional)"
|
|
221
|
+
placeholder="Breve descripción del objetivo"
|
|
222
|
+
value={desc}
|
|
223
|
+
onChange={(e) => setDesc(e.target.value)}
|
|
224
|
+
/>
|
|
225
|
+
|
|
226
|
+
<div className="flex justify-end gap-2 mt-4">
|
|
227
|
+
<Button
|
|
228
|
+
type="button"
|
|
229
|
+
variant="ghost"
|
|
230
|
+
onClick={() => setIsCreating(false)}
|
|
231
|
+
>
|
|
232
|
+
Cancelar
|
|
233
|
+
</Button>
|
|
234
|
+
<Button
|
|
235
|
+
type="submit"
|
|
236
|
+
variant="primary"
|
|
237
|
+
className="bg-purple-600 hover:bg-purple-700 text-white shadow-lg shadow-purple-200"
|
|
238
|
+
>
|
|
239
|
+
Crear Proyecto
|
|
240
|
+
</Button>
|
|
241
|
+
</div>
|
|
242
|
+
</form>
|
|
243
|
+
</Modal>
|
|
244
|
+
</div>
|
|
245
|
+
</Layout>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { useNavigate } from "react-router-dom";
|
|
2
|
+
|
|
3
|
+
export default function TermsPage() {
|
|
4
|
+
const navigate = useNavigate();
|
|
5
|
+
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
style={{
|
|
9
|
+
fontFamily: "'Inter', sans-serif",
|
|
10
|
+
color: "#111827",
|
|
11
|
+
backgroundColor: "#F5F7FB",
|
|
12
|
+
minHeight: "100vh",
|
|
13
|
+
padding: "4rem 2rem",
|
|
14
|
+
}}
|
|
15
|
+
>
|
|
16
|
+
<main
|
|
17
|
+
style={{
|
|
18
|
+
maxWidth: "700px",
|
|
19
|
+
margin: "0 auto",
|
|
20
|
+
background: "#fff",
|
|
21
|
+
padding: "2rem",
|
|
22
|
+
borderRadius: "24px",
|
|
23
|
+
boxShadow: "0 10px 40px rgba(0,0,0,0.05)",
|
|
24
|
+
}}
|
|
25
|
+
>
|
|
26
|
+
<button
|
|
27
|
+
onClick={() => navigate("/")}
|
|
28
|
+
style={{
|
|
29
|
+
marginBottom: "2rem",
|
|
30
|
+
background: "transparent",
|
|
31
|
+
border: "none",
|
|
32
|
+
color: "#7C6CFF",
|
|
33
|
+
fontWeight: 600,
|
|
34
|
+
cursor: "pointer",
|
|
35
|
+
}}
|
|
36
|
+
>
|
|
37
|
+
← Volver al inicio
|
|
38
|
+
</button>
|
|
39
|
+
|
|
40
|
+
<h1
|
|
41
|
+
style={{
|
|
42
|
+
fontSize: "2rem",
|
|
43
|
+
fontWeight: 800,
|
|
44
|
+
marginBottom: "2rem",
|
|
45
|
+
color: "#111827",
|
|
46
|
+
}}
|
|
47
|
+
>
|
|
48
|
+
Términos de uso de WADI
|
|
49
|
+
</h1>
|
|
50
|
+
|
|
51
|
+
<section style={{ marginBottom: "2rem" }}>
|
|
52
|
+
<h2
|
|
53
|
+
style={{
|
|
54
|
+
fontSize: "1.25rem",
|
|
55
|
+
fontWeight: 700,
|
|
56
|
+
marginBottom: "1rem",
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
1. Descripción del servicio
|
|
60
|
+
</h2>
|
|
61
|
+
<p style={{ lineHeight: 1.6, color: "#4B5563" }}>
|
|
62
|
+
WADI es una herramienta de asistencia basada en inteligencia
|
|
63
|
+
artificial diseñada para potenciar la productividad personal y la
|
|
64
|
+
gestión de proyectos, ofreciendo funcionalidades de chat,
|
|
65
|
+
planificación y tutoría adaptativa.
|
|
66
|
+
</p>
|
|
67
|
+
</section>
|
|
68
|
+
|
|
69
|
+
<section style={{ marginBottom: "2rem" }}>
|
|
70
|
+
<h2
|
|
71
|
+
style={{
|
|
72
|
+
fontSize: "1.25rem",
|
|
73
|
+
fontWeight: 700,
|
|
74
|
+
marginBottom: "1rem",
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
2. Uso permitido
|
|
78
|
+
</h2>
|
|
79
|
+
<p style={{ lineHeight: 1.6, color: "#4B5563" }}>
|
|
80
|
+
El usuario se compromete a no utilizar el servicio para fines
|
|
81
|
+
ilegales, dañinos o que violen derechos de terceros, ni para generar
|
|
82
|
+
contenido discriminatorio, violento o engañoso.
|
|
83
|
+
</p>
|
|
84
|
+
</section>
|
|
85
|
+
|
|
86
|
+
<section style={{ marginBottom: "2rem" }}>
|
|
87
|
+
<h2
|
|
88
|
+
style={{
|
|
89
|
+
fontSize: "1.25rem",
|
|
90
|
+
fontWeight: 700,
|
|
91
|
+
marginBottom: "1rem",
|
|
92
|
+
}}
|
|
93
|
+
>
|
|
94
|
+
3. Contenido generado por IA
|
|
95
|
+
</h2>
|
|
96
|
+
<p style={{ lineHeight: 1.6, color: "#4B5563" }}>
|
|
97
|
+
El contenido generado por WADI es orientativo y automatizado. La
|
|
98
|
+
inteligencia artificial puede cometer errores. El usuario es el
|
|
99
|
+
único responsable de verificar la exactitud y adecuación de la
|
|
100
|
+
información antes de tomar decisiones. No garantizamos la exactitud
|
|
101
|
+
absoluta de las respuestas.
|
|
102
|
+
</p>
|
|
103
|
+
</section>
|
|
104
|
+
|
|
105
|
+
<section style={{ marginBottom: "2rem" }}>
|
|
106
|
+
<h2
|
|
107
|
+
style={{
|
|
108
|
+
fontSize: "1.25rem",
|
|
109
|
+
fontWeight: 700,
|
|
110
|
+
marginBottom: "1rem",
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
4. Propiedad Intelectual
|
|
114
|
+
</h2>
|
|
115
|
+
<p style={{ lineHeight: 1.6, color: "#4B5563" }}>
|
|
116
|
+
La marca WADI y el software de la plataforma son propiedad de sus
|
|
117
|
+
desarrolladores. El usuario conserva la propiedad de los datos de
|
|
118
|
+
entrada y el contenido generado para su uso en sus propios
|
|
119
|
+
proyectos, en la medida en que la ley aplicable lo permita.
|
|
120
|
+
</p>
|
|
121
|
+
</section>
|
|
122
|
+
|
|
123
|
+
<section style={{ marginBottom: "2rem" }}>
|
|
124
|
+
<h2
|
|
125
|
+
style={{
|
|
126
|
+
fontSize: "1.25rem",
|
|
127
|
+
fontWeight: 700,
|
|
128
|
+
marginBottom: "1rem",
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
5. Cuentas y acceso
|
|
132
|
+
</h2>
|
|
133
|
+
<p style={{ lineHeight: 1.6, color: "#4B5563" }}>
|
|
134
|
+
El usuario es responsable de mantener la seguridad de sus
|
|
135
|
+
credenciales. WADI no se hace responsable por accesos no autorizados
|
|
136
|
+
derivados de negligencia del usuario. Nos reservamos el derecho de
|
|
137
|
+
suspender cuentas que violen estos términos.
|
|
138
|
+
</p>
|
|
139
|
+
</section>
|
|
140
|
+
|
|
141
|
+
<section style={{ marginBottom: "2rem" }}>
|
|
142
|
+
<h2
|
|
143
|
+
style={{
|
|
144
|
+
fontSize: "1.25rem",
|
|
145
|
+
fontWeight: 700,
|
|
146
|
+
marginBottom: "1rem",
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
6. Limitación de responsabilidad
|
|
150
|
+
</h2>
|
|
151
|
+
<p style={{ lineHeight: 1.6, color: "#4B5563" }}>
|
|
152
|
+
En la máxima medida permitida por la ley, WADI y sus desarrolladores
|
|
153
|
+
no serán responsables por daños directos, indirectos o consecuentes
|
|
154
|
+
que surjan del uso o la imposibilidad de uso del servicio.
|
|
155
|
+
</p>
|
|
156
|
+
</section>
|
|
157
|
+
|
|
158
|
+
<section style={{ marginBottom: "2rem" }}>
|
|
159
|
+
<h2
|
|
160
|
+
style={{
|
|
161
|
+
fontSize: "1.25rem",
|
|
162
|
+
fontWeight: 700,
|
|
163
|
+
marginBottom: "1rem",
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
7. Contacto
|
|
167
|
+
</h2>
|
|
168
|
+
<p style={{ lineHeight: 1.6, color: "#4B5563" }}>
|
|
169
|
+
Para cualquier consulta sobre estos términos, puede contactarnos en:{" "}
|
|
170
|
+
<a
|
|
171
|
+
href="mailto:contacto@wadi.app"
|
|
172
|
+
style={{ color: "#7C6CFF", fontWeight: 600 }}
|
|
173
|
+
>
|
|
174
|
+
contacto@wadi.app
|
|
175
|
+
</a>
|
|
176
|
+
</p>
|
|
177
|
+
</section>
|
|
178
|
+
|
|
179
|
+
<section>
|
|
180
|
+
<h2
|
|
181
|
+
style={{
|
|
182
|
+
fontSize: "1.25rem",
|
|
183
|
+
fontWeight: 700,
|
|
184
|
+
marginBottom: "1rem",
|
|
185
|
+
}}
|
|
186
|
+
>
|
|
187
|
+
8. Cambios en los términos
|
|
188
|
+
</h2>
|
|
189
|
+
<p style={{ lineHeight: 1.6, color: "#4B5563" }}>
|
|
190
|
+
Nos reservamos el derecho de modificar estos términos. Se
|
|
191
|
+
notificarán los cambios actualizando la fecha al pie de esta página.
|
|
192
|
+
</p>
|
|
193
|
+
<p
|
|
194
|
+
style={{ marginTop: "1rem", fontSize: "0.9rem", color: "#6B7280" }}
|
|
195
|
+
>
|
|
196
|
+
Última actualización: Diciembre 2025
|
|
197
|
+
</p>
|
|
198
|
+
</section>
|
|
199
|
+
</main>
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { createBrowserRouter } from "react-router-dom";
|
|
2
|
+
import { AuthLoader } from "./components/AuthLoader";
|
|
3
|
+
import Login from "./pages/Login";
|
|
4
|
+
import Projects from "./pages/Projects";
|
|
5
|
+
import ProjectDetail from "./pages/ProjectDetail";
|
|
6
|
+
import ChatPage from "./pages/ChatPage";
|
|
7
|
+
import { AuditReport } from "./components/auditor/AuditReport";
|
|
8
|
+
|
|
9
|
+
import IntroWadi from "./pages/IntroWadi";
|
|
10
|
+
import TermsPage from "./pages/TermsPage";
|
|
11
|
+
import PrivacyPage from "./pages/PrivacyPage";
|
|
12
|
+
import DashboardPage from "./pages/DashboardPage";
|
|
13
|
+
|
|
14
|
+
export const router = createBrowserRouter([
|
|
15
|
+
{
|
|
16
|
+
path: "/",
|
|
17
|
+
element: <IntroWadi />,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
path: "/terminos",
|
|
21
|
+
element: <TermsPage />,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
path: "/privacidad",
|
|
25
|
+
element: <PrivacyPage />,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
path: "/login",
|
|
29
|
+
element: (
|
|
30
|
+
<AuthLoader>
|
|
31
|
+
<Login />
|
|
32
|
+
</AuthLoader>
|
|
33
|
+
),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
path: "/projects",
|
|
37
|
+
element: (
|
|
38
|
+
<AuthLoader>
|
|
39
|
+
<Projects />
|
|
40
|
+
</AuthLoader>
|
|
41
|
+
),
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
path: "/projects/:id",
|
|
45
|
+
element: (
|
|
46
|
+
<AuthLoader>
|
|
47
|
+
<ProjectDetail />
|
|
48
|
+
</AuthLoader>
|
|
49
|
+
),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
path: "/chat",
|
|
53
|
+
element: (
|
|
54
|
+
<AuthLoader>
|
|
55
|
+
<ChatPage />
|
|
56
|
+
</AuthLoader>
|
|
57
|
+
),
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
path: "/chat/:conversationId",
|
|
61
|
+
element: (
|
|
62
|
+
<AuthLoader>
|
|
63
|
+
<ChatPage />
|
|
64
|
+
</AuthLoader>
|
|
65
|
+
),
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
path: "/chat/:conversationId/audit",
|
|
69
|
+
element: (
|
|
70
|
+
<AuthLoader>
|
|
71
|
+
<AuditReport />
|
|
72
|
+
</AuthLoader>
|
|
73
|
+
),
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
path: "/dashboard",
|
|
77
|
+
element: (
|
|
78
|
+
<AuthLoader>
|
|
79
|
+
<DashboardPage />
|
|
80
|
+
</AuthLoader>
|
|
81
|
+
),
|
|
82
|
+
},
|
|
83
|
+
]);
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { create } from "zustand";
|
|
2
|
+
import type { User } from "@supabase/supabase-js";
|
|
3
|
+
import { supabase } from "../config/supabase";
|
|
4
|
+
|
|
5
|
+
interface AuthResponse {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
+
data: any;
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
error: any;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface AuthState {
|
|
13
|
+
user: User | null;
|
|
14
|
+
loading: boolean;
|
|
15
|
+
signIn: (email: string, password: string) => Promise<AuthResponse>;
|
|
16
|
+
signUp: (
|
|
17
|
+
email: string,
|
|
18
|
+
password: string,
|
|
19
|
+
captchaToken?: string
|
|
20
|
+
) => Promise<AuthResponse>;
|
|
21
|
+
loginAsGuest: () => Promise<AuthResponse>;
|
|
22
|
+
convertGuestToUser: (
|
|
23
|
+
email: string,
|
|
24
|
+
password: string
|
|
25
|
+
) => Promise<AuthResponse>;
|
|
26
|
+
signOut: () => Promise<void>;
|
|
27
|
+
setUser: (user: User | null) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const useAuthStore = create<AuthState>((set) => ({
|
|
31
|
+
user: null,
|
|
32
|
+
loading: false,
|
|
33
|
+
|
|
34
|
+
setUser: (user) => set({ user }),
|
|
35
|
+
|
|
36
|
+
signIn: async (email, password) => {
|
|
37
|
+
set({ loading: true });
|
|
38
|
+
|
|
39
|
+
const { data, error } = await supabase.auth.signInWithPassword({
|
|
40
|
+
email,
|
|
41
|
+
password,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
set({ user: data.user, loading: false });
|
|
45
|
+
return { data, error };
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
signUp: async (email, password, captchaToken) => {
|
|
49
|
+
set({ loading: true });
|
|
50
|
+
|
|
51
|
+
const { data, error } = await supabase.auth.signUp({
|
|
52
|
+
email,
|
|
53
|
+
password,
|
|
54
|
+
options: { captchaToken },
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
set({ user: data.user, loading: false });
|
|
58
|
+
return { data, error };
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
loginAsGuest: async () => {
|
|
62
|
+
set({ loading: true });
|
|
63
|
+
const { data, error } = await supabase.auth.signInAnonymously();
|
|
64
|
+
if (error) console.error("Error login anónimo:", error);
|
|
65
|
+
set({ user: data.user, loading: false });
|
|
66
|
+
return { data, error };
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
convertGuestToUser: async (email, password) => {
|
|
70
|
+
set({ loading: true });
|
|
71
|
+
|
|
72
|
+
// 1. Capture current anon ID
|
|
73
|
+
const {
|
|
74
|
+
data: { session: anonSession },
|
|
75
|
+
} = await supabase.auth.getSession();
|
|
76
|
+
const anonUserId = anonSession?.user?.id;
|
|
77
|
+
|
|
78
|
+
// 2. Attempt to convert (Update User)
|
|
79
|
+
const { data, error } = await supabase.auth.updateUser({
|
|
80
|
+
email: email,
|
|
81
|
+
password: password,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (error) {
|
|
85
|
+
// 3. Handle "User already exists" conflict
|
|
86
|
+
// Note: Error message content may vary, checking for common indicators
|
|
87
|
+
if (
|
|
88
|
+
error.message.includes("already registered") ||
|
|
89
|
+
error.message.includes("unique constraint") ||
|
|
90
|
+
error.status === 422
|
|
91
|
+
) {
|
|
92
|
+
console.log("Email exists. Attempting to link/merge account...");
|
|
93
|
+
|
|
94
|
+
// 4. Sign in to the existing account
|
|
95
|
+
const { data: signInData, error: signInError } =
|
|
96
|
+
await supabase.auth.signInWithPassword({
|
|
97
|
+
email,
|
|
98
|
+
password,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (signInError) {
|
|
102
|
+
set({ loading: false });
|
|
103
|
+
throw new Error(
|
|
104
|
+
"El usuario ya existe y la contraseña es incorrecta."
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (signInData.user && anonUserId) {
|
|
109
|
+
console.log(
|
|
110
|
+
`Merging data from ${anonUserId} to ${signInData.user.id}`
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// 5. Reassign entities (Projects & Runs)
|
|
114
|
+
// Note: This relies on RLS policies allowing the new user to update the old user's rows
|
|
115
|
+
// OR the backend handling this. We attempt client-side update here as per request.
|
|
116
|
+
|
|
117
|
+
const { error: projectError } = await supabase
|
|
118
|
+
.from("projects")
|
|
119
|
+
.update({ user_id: signInData.user.id }) // Assuming 'user_id' column exists
|
|
120
|
+
.eq("user_id", anonUserId); // Assuming logical 'owner' link
|
|
121
|
+
|
|
122
|
+
if (projectError)
|
|
123
|
+
console.warn("Project merge warning:", projectError);
|
|
124
|
+
|
|
125
|
+
const { error: runError } = await supabase
|
|
126
|
+
.from("runs")
|
|
127
|
+
.update({ user_id: signInData.user.id })
|
|
128
|
+
.eq("user_id", anonUserId);
|
|
129
|
+
|
|
130
|
+
if (runError) console.warn("Run merge warning:", runError);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
set({ user: signInData.user, loading: false });
|
|
134
|
+
// Return null error to indicate success in handling
|
|
135
|
+
return { data: signInData, error: null };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Real error
|
|
139
|
+
set({ loading: false });
|
|
140
|
+
return { data, error };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Success (New User Conversion)
|
|
144
|
+
set({ user: data.user, loading: false });
|
|
145
|
+
return { data, error };
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
signOut: async () => {
|
|
149
|
+
await supabase.auth.signOut();
|
|
150
|
+
set({ user: null });
|
|
151
|
+
},
|
|
152
|
+
}));
|