Initial functional version of the portfolio chatbot site
Some checks failed
Deploy to GitHub Pages / build-and-deploy (push) Has been cancelled
Some checks failed
Deploy to GitHub Pages / build-and-deploy (push) Has been cancelled
This commit is contained in:
473
app.vue
Normal file
473
app.vue
Normal file
@@ -0,0 +1,473 @@
|
||||
<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);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(147, 51, 234, 0.7);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user