From 3cba7f8ab8c4216e051210e9560eea2dd7d8d5da Mon Sep 17 00:00:00 2001 From: JHAMILCALI Date: Fri, 27 Mar 2026 21:58:21 -0400 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20actualizar=20VITE=5FAPI=5FURL=20a?= =?UTF-8?q?=20la=20URL=20de=20producci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 2 +- .env.production | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.development b/.env.development index 5317fce..8a5b333 100644 --- a/.env.development +++ b/.env.development @@ -1 +1 @@ -VITE_API_URL=http://localhost:3000 +VITE_API_URL=https://sapient-lab-api-fkadhxgthve3hycv.eastus-01.azurewebsites.net diff --git a/.env.production b/.env.production index cd41370..2e00879 100644 --- a/.env.production +++ b/.env.production @@ -1 +1 @@ -VITE_API_URL=http://localhost:3000 \ No newline at end of file +VITE_API_URL=https://sapient-lab-api-fkadhxgthve3hycv.eastus-01.azurewebsites.net \ No newline at end of file From d0922d199c6b2bdc789ea0f8caa399ae0f873509 Mon Sep 17 00:00:00 2001 From: JHAMILCALI Date: Sat, 28 Mar 2026 00:21:24 -0400 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20agregar=20funcionalidad=20para=20su?= =?UTF-8?q?bir=20notas=20a=20la=20blockchain=20y=20mejorar=20la=20gesti?= =?UTF-8?q?=C3=B3n=20de=20estado?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 5 +- .env.production | 1 + src/pages/IntelligentLabNotebook.tsx | 91 +++++++++++++++++++++++++++- 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/.env.development b/.env.development index 8a5b333..56edb92 100644 --- a/.env.development +++ b/.env.development @@ -1 +1,4 @@ -VITE_API_URL=https://sapient-lab-api-fkadhxgthve3hycv.eastus-01.azurewebsites.net +#VITE_API_URL=https://sapient-lab-api-fkadhxgthve3hycv.eastus-01.azurewebsites.net +VITE_API_URL=http://localhost:3000 +API_BLOCKCHAIN=https://backend-blockchain-sapiens-lab.vercel.app +VITE_API_BLOCKCHAIN=https://backend-blockchain-sapiens-lab.vercel.app \ No newline at end of file diff --git a/.env.production b/.env.production index 447fa52..012d169 100644 --- a/.env.production +++ b/.env.production @@ -2,3 +2,4 @@ # Set this in CI/CD (or override locally) to the deployed NestJS endpoint. VITE_API_URL=https://sapient-lab-api-fkadhxgthve3hycv.eastus-01.azurewebsites.net VITE_API_TARGET=https://sapient-lab-api-fkadhxgthve3hycv.eastus-01.azurewebsites.net +VITE_API_BLOCKCHAIN=https://backend-blockchain-sapiens-lab.vercel.app diff --git a/src/pages/IntelligentLabNotebook.tsx b/src/pages/IntelligentLabNotebook.tsx index 280b0bc..a6ba38c 100644 --- a/src/pages/IntelligentLabNotebook.tsx +++ b/src/pages/IntelligentLabNotebook.tsx @@ -11,6 +11,7 @@ import { FiMic, FiMicOff, FiTrash2, + FiUploadCloud, } from 'react-icons/fi'; import { aiService } from '../services/aiService'; import { useTheme } from '../context/ThemeContext'; @@ -72,6 +73,8 @@ export default function IntelligentLabNotebook() { const [isSaving, setIsSaving] = useState(false); const [saveStatus, setSaveStatus] = useState<'idle' | 'saved' | 'error'>('idle'); const [noteTitle, setNoteTitle] = useState(''); + const [isUploadingBlockchain, setIsUploadingBlockchain] = useState(false); + const [blockchainStatus, setBlockchainStatus] = useState<'idle' | 'success' | 'error'>('idle'); const scrollRef = useRef(null); const noteTextareaRef = useRef(null); @@ -421,6 +424,66 @@ export default function IntelligentLabNotebook() { await generateSuggestions(noteIdToAnalyze); }; + const handleUploadToBlockchain = async () => { + if (!noteContent.trim() || isUploadingBlockchain) return; + setIsUploadingBlockchain(true); + setBlockchainStatus('idle'); + + try { + // 1. Guardar la nota para asegurar que existe en el backend y tiene ID real + const savedId = await saveNote(noteContent); + if (!savedId) throw new Error('No se pudo guardar la nota antes de mintear'); + + // 2. Obtener los datos reales de la nota desde el backend + const noteResponse = await fetch(`/api/experiment-notes/${savedId}`); + if (!noteResponse.ok) throw new Error(`Error al obtener nota: ${noteResponse.status}`); + const noteData = await noteResponse.json(); + + const title = noteData.title ?? noteTitle.trim() ?? deriveTitle(noteContent); + const description = noteData.content ?? noteContent; + const createdAt = noteData.created_at + ? new Date(noteData.created_at).toISOString().replace('T', ' ').substring(0, 23) + : new Date().toISOString().replace('T', ' ').substring(0, 23); + const publishedAt = new Date().toISOString().replace('T', ' ').substring(0, 23); + const userName = localStorage.getItem('sapientlab_user_name') ?? 'Desconocido'; + + // 3. Construir el payload exacto que espera el endpoint + const payload = { + name: title, + description, + image: 'https://moccasin-magnetic-gopher-766.mypinata.cloud/ipfs/bafybeiagdk4wzi4pz6sbzytf6w2b5kxj6idyex5lsuzkfcu7lngo6rinjm', + attributes: [ + { trait_type: 'ID', value: `EXP-${savedId}` }, + { trait_type: 'Usuario', value: userName }, + { trait_type: 'Creado', value: createdAt }, + { trait_type: 'publicado', value: publishedAt }, + ], + }; + + // 4. Subir a blockchain + const blockchainUrl = import.meta.env.VITE_API_BLOCKCHAIN ?? 'https://backend-blockchain-sapiens-lab.vercel.app'; + const response = await fetch(`${blockchainUrl}/pinata/upload-json`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + + if (!response.ok) { + const errData = await response.json().catch(() => null); + throw new Error(errData?.message ?? `Error ${response.status}`); + } + + setBlockchainStatus('success'); + setTimeout(() => setBlockchainStatus('idle'), 3000); + } catch (error) { + console.error('Error subiendo a blockchain:', error); + setBlockchainStatus('error'); + setTimeout(() => setBlockchainStatus('idle'), 3000); + } finally { + setIsUploadingBlockchain(false); + } + }; + const generateSuggestions = async (noteId: number) => { setIsGeneratingSuggestions(true); try { @@ -751,12 +814,17 @@ ${sugg.safetyWarnings.length > 0 ? `### Advertencias de Seguridad\n${sugg.safety placeholder="Escribe tus notas del experimento aquí. Luego usa 'Analizar texto' para pedir sugerencias a la IA..." className="flex-1 p-4 rounded-xl resize-none font-mono text-sm focus:outline-none focus:ring-2 focus:ring-accent/50 bg-[#0a0f1c] border border-accent/20 text-slate-200 placeholder:text-slate-600 transition-all duration-300" /> -
+
{isGeneratingSuggestions && ( Analizando... )} + {isUploadingBlockchain && ( + + Subiendo a blockchain... + + )} +
+ + {/* Banner Auditar Nota en Blockchain */} + {blockchainStatus === 'success' && ( +
+
+ +
+
+

Nota minteada en Somnia Network

+ + Auditar Notas → + +
+ +
+ )}
From 1cbb81e45d5f51b5c8512374ae8dc0ecaaff7048 Mon Sep 17 00:00:00 2001 From: JHAMILCALI Date: Sat, 28 Mar 2026 01:24:04 -0400 Subject: [PATCH 4/4] refactor: remove unused lastMintedTokenId state and streamline blockchain upload handling in IntelligentLabNotebook component --- src/pages/IntelligentLabNotebook.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pages/IntelligentLabNotebook.tsx b/src/pages/IntelligentLabNotebook.tsx index 000ee39..914f837 100644 --- a/src/pages/IntelligentLabNotebook.tsx +++ b/src/pages/IntelligentLabNotebook.tsx @@ -73,7 +73,6 @@ export default function IntelligentLabNotebook() { const [noteTitle, setNoteTitle] = useState(''); const [isUploadingBlockchain, setIsUploadingBlockchain] = useState(false); const [blockchainStatus, setBlockchainStatus] = useState<'idle' | 'success' | 'error'>('idle'); - const [lastMintedTokenId, setLastMintedTokenId] = useState(null); const scrollRef = useRef(null); const noteTextareaRef = useRef(null); @@ -472,8 +471,7 @@ export default function IntelligentLabNotebook() { throw new Error(errData?.message ?? `Error ${response.status}`); } - const result = await response.json(); - setLastMintedTokenId(result?.nft?.tokenId ?? null); + await response.json(); setBlockchainStatus('success'); } catch (error) { console.error('Error subiendo a blockchain:', error); @@ -900,7 +898,7 @@ ${sugg.safetyWarnings.length > 0 ? `### Advertencias de Seguridad\n${sugg.safety