476 lines
16 KiB
Vue
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>
|