476 lines
16 KiB
Vue

<template>
<div class="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 text-white">
<!-- Header -->
<header class="border-b border-white/10 backdrop-blur-sm bg-black/20">
<div class="container mx-auto px-4 py-4 flex justify-between items-center">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 bg-gradient-to-r from-purple-500 to-pink-500 rounded-full flex items-center justify-center">
<span class="text-lg font-bold">AI</span>
</div>
<div>
<h1 class="text-xl font-bold">Portfolio Assistant</h1>
<p class="text-sm text-gray-300">Powered by Advanced AI</p>
</div>
</div>
<button
@click="toggleTheme"
class="p-2 rounded-lg bg-white/10 hover:bg-white/20 transition-colors"
>
<component :is="isDark ? 'Sun' : 'Moon'" class="w-5 h-5" />
</button>
</div>
</header>
<div class="container mx-auto px-4 py-8 max-w-4xl">
<!-- Welcome Section -->
<div v-if="messages.length === 0" class="text-center mb-8">
<div class="mb-6">
<div class="w-24 h-24 bg-gradient-to-r from-purple-500 to-pink-500 rounded-full mx-auto mb-4 flex items-center justify-center">
<Bot class="w-12 h-12" />
</div>
<h2 class="text-3xl font-bold mb-2">¡Hola! Soy tu Asistente de Portfolio</h2>
<p class="text-gray-300 text-lg">Pregúntame sobre experiencia, habilidades, proyectos o cualquier cosa técnica</p>
</div>
<!-- Quick Actions -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-8">
<button
v-for="suggestion in quickSuggestions"
:key="suggestion.text"
@click="sendMessage(suggestion.text)"
class="p-4 bg-white/5 hover:bg-white/10 rounded-xl border border-white/10 transition-all hover:scale-105 text-left"
>
<component :is="suggestion.icon" class="w-6 h-6 mb-2 text-purple-400" />
<h3 class="font-semibold mb-1">{{ suggestion.title }}</h3>
<p class="text-sm text-gray-400">{{ suggestion.text }}</p>
</button>
</div>
</div>
<!-- Chat Messages -->
<div class="space-y-4 mb-6" ref="messagesContainer">
<div
v-for="message in messages"
:key="message.id"
class="flex items-start space-x-3"
:class="message.role === 'user' ? 'flex-row-reverse space-x-reverse' : ''"
>
<div class="flex-shrink-0">
<div
class="w-8 h-8 rounded-full flex items-center justify-center"
:class="message.role === 'user'
? 'bg-gradient-to-r from-blue-500 to-cyan-500'
: 'bg-gradient-to-r from-purple-500 to-pink-500'"
>
<component :is="message.role === 'user' ? 'User' : 'Bot'" class="w-4 h-4" />
</div>
</div>
<div
class="max-w-xs lg:max-w-md px-4 py-2 rounded-2xl"
:class="message.role === 'user'
? 'bg-gradient-to-r from-blue-500 to-cyan-500 text-white'
: 'bg-white/10 backdrop-blur-sm border border-white/20'"
>
<div v-if="message.role === 'assistant' && message.typing" class="flex space-x-1">
<div class="w-2 h-2 bg-purple-400 rounded-full animate-bounce"></div>
<div class="w-2 h-2 bg-purple-400 rounded-full animate-bounce" style="animation-delay: 0.1s"></div>
<div class="w-2 h-2 bg-purple-400 rounded-full animate-bounce" style="animation-delay: 0.2s"></div>
</div>
<div v-else v-html="formatMessage(message.content)"></div>
</div>
</div>
</div>
<!-- Input Form -->
<form @submit.prevent="handleSubmit" class="relative">
<div class="flex space-x-2">
<input
v-model="input"
:disabled="isLoading"
placeholder="Pregúntame sobre mi experiencia, habilidades, proyectos..."
class="flex-1 px-4 py-3 bg-white/10 backdrop-blur-sm border border-white/20 rounded-xl focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent placeholder-gray-400"
/>
<button
type="submit"
:disabled="isLoading || !input.trim()"
class="px-6 py-3 bg-gradient-to-r from-purple-500 to-pink-500 rounded-xl font-semibold disabled:opacity-50 disabled:cursor-not-allowed hover:from-purple-600 hover:to-pink-600 transition-all"
>
<Send class="w-5 h-5" />
</button>
</div>
</form>
<!-- Tech Stack Display -->
<div class="mt-8 p-6 bg-white/5 backdrop-blur-sm rounded-xl border border-white/10">
<h3 class="text-lg font-semibold mb-4 flex items-center">
<Code class="w-5 h-5 mr-2 text-purple-400" />
Stack Tecnológico Principal
</h3>
<div class="flex flex-wrap gap-2">
<span v-for="tech in techStack" :key="tech"
class="px-3 py-1 bg-purple-500/20 text-purple-300 rounded-full text-sm border border-purple-500/30">
{{ tech }}
</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, nextTick, onMounted } from 'vue'
import { Bot, User, Send, Sun, Moon, Code, Briefcase, Award, Rocket } from 'lucide-vue-next'
const messages = ref([])
const input = ref('')
const isLoading = ref(false)
const isDark = ref(true)
const messagesContainer = ref(null)
const techStack = [
'Vue.js', 'React', 'Node.js', 'TypeScript', 'Python', 'Docker',
'AWS', 'MongoDB', 'PostgreSQL', 'Git', 'CI/CD', 'Microservicios'
]
const quickSuggestions = [
{
icon: 'Briefcase',
title: 'Experiencia',
text: '¿Cuál es tu experiencia laboral?'
},
{
icon: 'Code',
title: 'Habilidades',
text: '¿Qué tecnologías dominas?'
},
{
icon: 'Rocket',
title: 'Proyectos',
text: 'Cuéntame sobre tus proyectos destacados'
}
]
// Base de conocimiento pre-programada
const knowledgeBase = {
experiencia: {
keywords: ['experiencia', 'trabajo', 'laboral', 'años', 'empresa', 'puesto'],
response: `
<strong>💼 Experiencia Profesional</strong><br><br>
<strong>Senior Full Stack Developer</strong> (2021 - Presente)<br>
• Liderazgo de equipo de 5 desarrolladores<br>
• Arquitectura de microservicios con Node.js y Docker<br>
• Implementación de CI/CD reduciendo deploys en 80%<br><br>
<strong>Frontend Developer</strong> (2019 - 2021)<br>
• Desarrollo de SPAs con Vue.js y React<br>
• Optimización de performance (Core Web Vitals)<br>
• Colaboración con equipos UX/UI<br><br>
<strong>Junior Developer</strong> (2018 - 2019)<br>
• Desarrollo de APIs REST con Express.js<br>
• Integración con bases de datos SQL y NoSQL<br>
• Metodologías ágiles (Scrum/Kanban)
`
},
habilidades: {
keywords: ['habilidades', 'tecnologías', 'stack', 'lenguajes', 'frameworks', 'dominas'],
response: `
<strong>🚀 Stack Tecnológico</strong><br><br>
<strong>Frontend:</strong><br>
• Vue.js 3 (Composition API, Pinia) - Avanzado<br>
• React (Hooks, Context, Redux) - Avanzado<br>
• TypeScript - Avanzado<br>
• Tailwind CSS, SCSS - Avanzado<br><br>
<strong>Backend:</strong><br>
• Node.js (Express, Fastify) - Avanzado<br>
• Python (Django, FastAPI) - Intermedio<br>
• Bases de datos: PostgreSQL, MongoDB - Avanzado<br><br>
<strong>DevOps & Cloud:</strong><br>
• Docker, Kubernetes - Intermedio<br>
• AWS (EC2, S3, Lambda) - Intermedio<br>
• CI/CD (GitHub Actions, Jenkins) - Avanzado
`
},
proyectos: {
keywords: ['proyectos', 'desarrollado', 'creado', 'portfolio', 'destacados'],
response: `
<strong>🎯 Proyectos Destacados</strong><br><br>
<strong>E-commerce Platform</strong><br>
• Plataforma completa con Vue.js + Node.js<br>
• +50,000 usuarios activos mensuales<br>
• Integración con Stripe y PayPal<br>
• <em>Tech:</em> Vue 3, Express, PostgreSQL, Redis<br><br>
<strong>Real-time Analytics Dashboard</strong><br>
• Dashboard en tiempo real con WebSockets<br>
• Procesamiento de +1M eventos/día<br>
• Visualizaciones interactivas con D3.js<br>
• <em>Tech:</em> React, Socket.io, InfluxDB<br><br>
<strong>Microservices Architecture</strong><br>
• Migración de monolito a microservicios<br>
• Reducción de latencia en 60%<br>
• Implementación con Docker y Kubernetes<br>
• <em>Tech:</em> Node.js, Docker, AWS EKS
`
},
educacion: {
keywords: ['educación', 'estudios', 'universidad', 'carrera', 'certificaciones'],
response: `
<strong>🎓 Formación Académica</strong><br><br>
<strong>Ingeniería en Sistemas</strong><br>
Universidad Tecnológica Nacional (2014-2018)<br>
• Especialización en Desarrollo de Software<br>
• Proyecto final: Sistema de gestión hospitalaria<br><br>
<strong>Certificaciones:</strong><br>
• AWS Certified Developer Associate (2022)<br>
• MongoDB Certified Developer (2021)<br>
• Scrum Master Certified (2020)<br><br>
<strong>Formación Continua:</strong><br>
• Cursos especializados en arquitectura de software<br>
• Participación en conferencias tech (JSConf, VueConf)<br>
• Contribuciones a proyectos open source
`
},
contacto: {
keywords: ['contacto', 'email', 'linkedin', 'github', 'cv'],
response: `
<strong>📞 Información de Contacto</strong><br><br>
<strong>Email:</strong> tu.email@ejemplo.com<br>
<strong>LinkedIn:</strong> linkedin.com/in/tu-perfil<br>
<strong>GitHub:</strong> github.com/tu-usuario<br>
<strong>Portfolio:</strong> tu-portfolio.com<br><br>
<strong>Disponibilidad:</strong><br>
• Disponible para nuevas oportunidades<br>
• Modalidad: Remoto/Híbrido/Presencial<br>
• Ubicación: Ciudad, País<br><br>
<em>¡No dudes en contactarme para discutir oportunidades!</em>
`
},
salario: {
keywords: ['salario', 'sueldo', 'pretensiones', 'económicas', 'remuneración'],
response: `
<strong>💰 Expectativas Salariales</strong><br><br>
Mis expectativas salariales son competitivas y están alineadas con:<br><br>
• Mi experiencia de +5 años en desarrollo<br>
• El mercado actual para Senior Developers<br>
• La complejidad y responsabilidades del rol<br>
• Los beneficios adicionales ofrecidos<br><br>
<em>Estoy abierto a discutir una propuesta integral que incluya salario base, beneficios y oportunidades de crecimiento.</em><br><br>
<strong>Factores importantes para mí:</strong><br>
• Crecimiento profesional<br>
• Ambiente de trabajo colaborativo<br>
• Flexibilidad horaria<br>
• Proyectos desafiantes
`
}
}
const defaultResponses = [
"Esa es una excelente pregunta. Como desarrollador senior, siempre busco mantenerme actualizado con las últimas tecnologías y mejores prácticas.",
"Interesante punto. En mi experiencia, he encontrado que la clave está en encontrar el equilibrio entre innovación y estabilidad.",
"Desde mi perspectiva técnica, considero que es fundamental evaluar cada herramienta en función del contexto específico del proyecto.",
"Basándome en mi experiencia en proyectos enterprise, puedo decir que la escalabilidad y mantenibilidad son aspectos cruciales.",
"Como alguien que ha trabajado tanto en startups como en empresas grandes, he aprendido a adaptar mi enfoque según las necesidades del negocio."
]
function findBestResponse(message) {
const lowerMessage = message.toLowerCase()
for (const [category, data] of Object.entries(knowledgeBase)) {
if (data.keywords.some(keyword => lowerMessage.includes(keyword))) {
return data.response
}
}
// Respuestas contextuales adicionales
if (lowerMessage.includes('react') || lowerMessage.includes('vue')) {
return `
<strong>⚛️ React vs Vue.js</strong><br><br>
Tengo experiencia sólida con ambos frameworks:<br><br>
<strong>React:</strong><br>
• Excelente ecosistema y comunidad<br>
• Hooks y Context API para gestión de estado<br>
• Ideal para aplicaciones complejas<br><br>
<strong>Vue.js:</strong><br>
• Curva de aprendizaje más suave<br>
• Composition API muy potente<br>
• Excelente para desarrollo rápido<br><br>
<em>La elección depende del proyecto, equipo y requisitos específicos.</em>
`
}
if (lowerMessage.includes('node') || lowerMessage.includes('backend')) {
return `
<strong>🔧 Desarrollo Backend</strong><br><br>
Mi experiencia en backend incluye:<br><br>
• <strong>Node.js:</strong> Express, Fastify, NestJS<br>
• <strong>APIs:</strong> REST, GraphQL, WebSockets<br>
• <strong>Bases de datos:</strong> PostgreSQL, MongoDB, Redis<br>
• <strong>Arquitectura:</strong> Microservicios, Event-driven<br>
• <strong>Testing:</strong> Jest, Mocha, Supertest<br><br>
<em>Siempre enfocado en código limpio, escalable y bien documentado.</em>
`
}
// Respuesta por defecto
return defaultResponses[Math.floor(Math.random() * defaultResponses.length)]
}
function formatMessage(content) {
return content.replace(/\n/g, '<br>')
}
async function sendMessage(text = null) {
const messageText = text || input.value.trim()
if (!messageText) return
// Agregar mensaje del usuario
const userMessage = {
id: Date.now(),
role: 'user',
content: messageText
}
messages.value.push(userMessage)
// Limpiar input
input.value = ''
isLoading.value = true
// Scroll to bottom
await nextTick()
scrollToBottom()
// Agregar mensaje de typing
const typingMessage = {
id: Date.now() + 1,
role: 'assistant',
content: '',
typing: true
}
messages.value.push(typingMessage)
await nextTick()
scrollToBottom()
// Simular delay de respuesta
setTimeout(() => {
// Remover mensaje de typing
messages.value.pop()
// Agregar respuesta real
const response = findBestResponse(messageText)
const assistantMessage = {
id: Date.now() + 2,
role: 'assistant',
content: response
}
messages.value.push(assistantMessage)
isLoading.value = false
nextTick(() => scrollToBottom())
}, 1500 + Math.random() * 1000) // Delay variable para mayor realismo
}
function handleSubmit() {
sendMessage()
}
function scrollToBottom() {
if (messagesContainer.value) {
messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight
}
}
function toggleTheme() {
isDark.value = !isDark.value
}
onMounted(() => {
// Mensaje de bienvenida después de un momento
setTimeout(() => {
const welcomeMessage = {
id: Date.now(),
role: 'assistant',
content: `
¡Hola! 👋 Soy tu asistente de portfolio inteligente.<br><br>
Puedo contarte sobre:<br>
• 💼 Mi experiencia profesional<br>
• 🚀 Habilidades técnicas<br>
• 🎯 Proyectos destacados<br>
• 🎓 Formación académica<br>
• 📞 Información de contacto<br><br>
<em>¿Qué te gustaría saber?</em>
`
}
messages.value.push(welcomeMessage)
nextTick(() => scrollToBottom())
}, 1000)
})
</script>
<style scoped>
@keyframes bounce {
0%, 80%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(-10px);
}
}
.animate-bounce {
animation: bounce 1s infinite;
}
/* Scrollbar personalizado */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
}
::-webkit-scrollbar-thumb {
/*background: rgba(147, 51, 234, 0.5);*/
background: rgba(59, 130, 246, 0.5); /* Blue-500 */
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
/*background: rgba(147, 51, 234, 0.7);*7
background: rgba(59, 130, 246, 0.7); /* Blue-500 */
}
</style>