14: Explora más componentes y agrega contenido a tu portafolio con Laravel


Introducción

En este tutorial vamos a explorar los componentes adicionales que Laravel Jetstream nos proporciona y los usaremos para agregar contenido y funcionalidad a nuestro portafolio. Aprenderemos a utilizar el componente Button, mejoraremos la presentación de nuestras secciones y añadiremos elementos interactivos que harán más atractiva nuestra página.


Paso 1: Explorar los componentes de Jetstream

Laravel Jetstream viene con una serie de componentes Vue predefinidos que podemos reutilizar. Vamos a revisar los más importantes:

1.1 Componentes disponibles en Jetstream

text
resources/js/Components/
├── ApplicationLogo.vue          # Logo de la aplicación
├── AuthenticationCard.vue       # Tarjeta para autenticación
├── Button.vue                    # Botón estilizado
├── Checkbox.vue                 # Checkbox personalizado
├── DangerButton.vue             # Botón de peligro/eliminar
├── Dropdown.vue                 # Menú desplegable
├── DropdownLink.vue             # Enlace dentro de dropdown
├── Input.vue                    # Campo de entrada
├── InputError.vue               # Mensaje de error
├── Label.vue                    # Etiqueta de formulario
├── Modal.vue                    # Ventana modal
├── NavLink.vue                  # Enlace de navegación
├── ResponsiveNavLink.vue        # Enlace responsive
├── SecondaryButton.vue          # Botón secundario
├── SectionBorder.vue            # Borde de sección
├── ValidationErrors.vue         # Errores de validación
└── Welcome.vue                  # Página de bienvenida

Paso 2: Usar el componente Button

El componente Button de Jetstream ya tiene estilos predefinidos, pero podemos personalizarlos fácilmente.

2.1 Importar el componente

vue
<script setup>
import { Head, Link } from '@inertiajs/vue3';
import Button from '@/Components/Button.vue';
// ... otros imports
</script>

2.2 Usar el componente con estilos personalizados

vue
<template>
    <Button class="bg-green-400 rounded-full font-bold text-sm text-gray-800 hover:bg-green-500 transition-colors">
        Let's Chat
    </Button>
</template>

2.3 Diferentes variantes de botones

Jetstream también proporciona otros tipos de botones:

vue
<template>
    <!-- Botón primario (indigo) -->
    <Button>Guardar</Button>
    
    <!-- Botón secundario (blanco con borde) -->
    <SecondaryButton>Cancelar</SecondaryButton>
    
    <!-- Botón de peligro (rojo) -->
    <DangerButton @click="delete">Eliminar</DangerButton>
    
    <!-- Botón personalizado -->
    <Button class="bg-purple-500 hover:bg-purple-700 text-white">
        Ver Proyectos
    </Button>
</template>

<script setup>
import Button from '@/Components/Button.vue';
import SecondaryButton from '@/Components/SecondaryButton.vue';
import DangerButton from '@/Components/DangerButton.vue';
</script>

Paso 3: Mejorar el Header con Call to Action

Vamos a mejorar la sección de cabecera agregando un llamado a la acción (CTA) atractivo.

3.1 Estructura del Header mejorado

vue
<template>
    <Section background="gray-800" min-height>
        <div class="flex flex-col justify-between h-full py-8">
            <!-- Contenido principal -->
            <div>
                <h1 class="border-b-2 border-gray-400 font-bold text-2xl uppercase text-gray-300 pb-3">
                    Hola, soy {{ user?.name || 'Juan' }}
                </h1>
                <p class="text-xl text-gray-400 mt-4">
                    Soy un Software Engineer y me encantaría trabajar contigo
                </p>
            </div>
            
            <!-- Llamado a la acción -->
            <div class="flex justify-end items-center border-b border-gray-500 pb-4">
                <p class="font-bold text-gray-300 text-xl mr-5">
                    ¿Quieres conocer más? ¡Hablemos!
                </p>
                <Button 
                    class="bg-green-400 rounded-full font-bold text-sm text-gray-800 px-6 py-3 hover:bg-green-500 transition-colors"
                >
                    Let's Chat
                </Button>
            </div>
            
            <!-- Flecha hacia abajo -->
            <div class="animate-bounce text-gray-300 text-center text-9xl mt-3">
                <a href="#skills">&#8675;</a>
            </div>
        </div>
    </Section>
</template>

<script setup>
import { usePage } from '@inertiajs/vue3';
import Section from '@/Components/Section.vue';
import Button from '@/Components/Button.vue';

const page = usePage();
const user = page.props.auth.user;
</script>

Paso 4: Agregar CTA en la sección de Skills

Vamos a añadir un botón de "Contactar" en la sección de habilidades para que los visitantes puedan contactarnos fácilmente.

vue
<template>
    <Section background="gray-100" text="gray-800" id="skills">
        <h2 class="text-4xl font-bold mt-8">
            Mis Habilidades
        </h2>
        
        <!-- Grid de habilidades -->
        <div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6 mt-8">
            <div v-for="skill in skills" :key="skill.id"
                 class="p-4 bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow">
                <div class="flex items-center justify-between">
                    <span class="font-medium">{{ skill.name }}</span>
                    <span class="text-sm text-gray-500">{{ skill.level }}%</span>
                </div>
                <div class="w-full bg-gray-200 rounded-full h-2 mt-2">
                    <div class="h-2 rounded-full transition-all duration-500"
                         :class="`bg-${skill.color}-500`"
                         :style="{ width: `${skill.level}%` }">
                    </div>
                </div>
            </div>
        </div>
        
        <!-- Call to Action -->
        <div class="flex justify-center mt-10">
            <Button 
                class="bg-indigo-500 hover:bg-indigo-700 text-white px-8 py-3 rounded-full font-bold transition-colors"
            >
                Get in Touch
            </Button>
        </div>
    </Section>
</template>

<script setup>
import Section from '@/Components/Section.vue';
import Button from '@/Components/Button.vue';

defineProps({
    skills: Array
});
</script>

Paso 5: Agregar CTA en la sección de Proyectos

De manera similar, añadimos un botón de "Saber más" en la sección de proyectos.

vue
<template>
    <Section background="gray-800" text="white" id="projects">
        <h2 class="text-4xl font-bold mt-8">
            Proyectos Destacados
        </h2>
        
        <!-- Grid de proyectos -->
        <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mt-8">
            <div v-for="project in projects" :key="project.id"
                 class="bg-white rounded-xl overflow-hidden shadow-lg hover:shadow-2xl transition-shadow">
                <img :src="project.image_url" :alt="project.title" class="w-full h-48 object-cover">
                <div class="p-6">
                    <h3 class="text-xl font-bold text-gray-800 mb-2">
                        {{ project.title }}
                    </h3>
                    <p class="text-gray-600 mb-4">{{ project.description }}</p>
                    <div class="flex flex-wrap gap-2 mb-4">
                        <span v-for="tech in project.technologies" :key="tech"
                              class="px-3 py-1 bg-gray-200 rounded-full text-sm text-gray-700">
                            {{ tech }}
                        </span>
                    </div>
                    <Link :href="route('projects.show', project.slug)"
                          class="inline-block px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700">
                        Ver más
                    </Link>
                </div>
            </div>
        </div>
        
        <!-- Call to Action -->
        <div class="flex justify-center mt-10">
            <Button 
                class="bg-purple-100 hover:bg-purple-800 text-gray-800 hover:text-white px-8 py-3 rounded-full font-bold transition-all duration-300"
            >
                Saber más al respecto
            </Button>
        </div>
    </Section>
</template>

<script setup>
import { Link } from '@inertiajs/vue3';
import Section from '@/Components/Section.vue';
import Button from '@/Components/Button.vue';

defineProps({
    projects: Array
});
</script>

Paso 6: Usar el componente Dropdown para el menú de usuario

Cuando el usuario está autenticado, Jetstream proporciona un menú desplegable con opciones de perfil.

vue
<template>
    <div class="flex items-center space-x-4">
        <!-- Botón de perfil con dropdown -->
        <Dropdown align="right" width="48">
            <template #trigger>
                <button class="flex items-center text-sm text-gray-300 hover:text-white">
                    <img :src="user.profile_photo_url" :alt="user.name" class="w-8 h-8 rounded-full mr-2">
                    {{ user.name }}
                    <svg class="ml-2 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
                    </svg>
                </button>
            </template>
            
            <template #content>
                <div class="py-1">
                    <DropdownLink :href="route('profile.show')">
                        Mi Perfil
                    </DropdownLink>
                    <DropdownLink :href="route('dashboard')">
                        Dashboard
                    </DropdownLink>
                    <DropdownLink :href="route('logout')" method="post" as="button">
                        Cerrar Sesión
                    </DropdownLink>
                </div>
            </template>
        </Dropdown>
    </div>
</template>

<script setup>
import Dropdown from '@/Components/Dropdown.vue';
import DropdownLink from '@/Components/DropdownLink.vue';

defineProps({
    user: Object
});
</script>

Paso 7: Usar el componente Modal para mostrar información

Los modales son útiles para mostrar información adicional sin navegar a otra página.

vue
<template>
    <div>
        <!-- Botón para abrir modal -->
        <Button @click="openModal = true">
            Ver detalles
        </Button>
        
        <!-- Modal -->
        <Modal :show="openModal" @close="openModal = false">
            <div class="p-6">
                <h2 class="text-2xl font-bold mb-4">
                    Detalles del Proyecto
                </h2>
                <p class="text-gray-600">
                    {{ projectContent }}
                </p>
                <div class="mt-6 flex justify-end">
                    <Button @click="openModal = false" class="bg-gray-200 text-gray-800 hover:bg-gray-300">
                        Cerrar
                    </Button>
                </div>
            </div>
        </Modal>
    </div>
</template>

<script setup>
import { ref } from 'vue';
import Modal from '@/Components/Modal.vue';
import Button from '@/Components/Button.vue';

const openModal = ref(false);
const projectContent = 'Este es el contenido detallado del proyecto...';
</script>

Paso 8: Crear un layout reutilizable

Para mantener consistencia en todas las páginas, vamos a crear un layout principal.

8.1 Crear AppLayout.vue

vue
<template>
    <div class="min-h-screen bg-gray-100">
        <nav class="bg-white border-b border-gray-100">
            <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
                <div class="flex justify-between h-16">
                    <div class="flex">
                        <!-- Logo -->
                        <div class="flex-shrink-0 flex items-center">
                            <Link :href="route('home')">
                                <ApplicationLogo class="block h-9 w-auto" />
                            </Link>
                        </div>
                        
                        <!-- Enlaces de navegación -->
                        <div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
                            <NavLink :href="route('home')" :active="route().current('home')">
                                Inicio
                            </NavLink>
                            <NavLink :href="route('projects.index')" :active="route().current('projects.*')">
                                Proyectos
                            </NavLink>
                            <NavLink :href="route('skills.index')" :active="route().current('skills.*')">
                                Habilidades
                            </NavLink>
                        </div>
                    </div>
                    
                    <!-- Menú de usuario -->
                    <div class="flex items-center space-x-4">
                        <template v-if="$page.props.auth.user">
                            <Dropdown align="right" width="48">
                                <template #trigger>
                                    <button class="flex items-center text-sm text-gray-700 hover:text-gray-900">
                                        <img :src="$page.props.auth.user.profile_photo_url" 
                                             :alt="$page.props.auth.user.name" 
                                             class="w-8 h-8 rounded-full mr-2">
                                        {{ $page.props.auth.user.name }}
                                    </button>
                                </template>
                                <template #content>
                                    <DropdownLink :href="route('profile.show')">
                                        Mi Perfil
                                    </DropdownLink>
                                    <DropdownLink :href="route('logout')" method="post" as="button">
                                        Cerrar Sesión
                                    </DropdownLink>
                                </template>
                            </Dropdown>
                        </template>
                        <template v-else>
                            <Link :href="route('login')" class="text-gray-700 hover:text-gray-900">
                                Iniciar Sesión
                            </Link>
                            <Link :href="route('register')" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">
                                Registrarse
                            </Link>
                        </template>
                    </div>
                </div>
            </div>
        </nav>

        <!-- Contenido de la página -->
        <main>
            <slot />
        </main>
        
        <!-- Footer -->
        <footer class="bg-gray-900 text-gray-400 px-12 py-8">
            <div class="flex justify-between items-center">
                <p>&copy; 2024 {{ $page.props.auth.user?.name || 'Juan' }}. Todos los derechos reservados.</p>
                <div class="flex space-x-6">
                    <a href="https://github.com" target="_blank" class="hover:text-white transition-colors">
                        GitHub
                    </a>
                    <a href="https://twitter.com" target="_blank" class="hover:text-white transition-colors">
                        Twitter
                    </a>
                    <a href="https://linkedin.com" target="_blank" class="hover:text-white transition-colors">
                        LinkedIn
                    </a>
                </div>
            </div>
        </footer>
    </div>
</template>

<script setup>
import { Link } from '@inertiajs/vue3';
import ApplicationLogo from '@/Components/ApplicationLogo.vue';
import NavLink from '@/Components/NavLink.vue';
import Dropdown from '@/Components/Dropdown.vue';
import DropdownLink from '@/Components/DropdownLink.vue';
</script>

8.2 Usar el layout en las páginas

vue
<template>
    <AppLayout>
        <Head title="Inicio" />
        
        <!-- Contenido de la página -->
        <div class="py-12">
            <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
                <div class="bg-white overflow-hidden shadow-xl sm:rounded-lg p-6">
                    <h1 class="text-3xl font-bold">Bienvenido</h1>
                    <p class="mt-4 text-gray-600">Este es el contenido de la página.</p>
                </div>
            </div>
        </div>
    </AppLayout>
</template>

<script setup>
import { Head } from '@inertiajs/vue3';
import AppLayout from '@/Layouts/AppLayout.vue';
</script>

Solución de problemas comunes

El botón no muestra los estilos correctos

Asegúrate de que el componente Button esté importado y registrado correctamente.

Los colores no se aplican

Verifica que los colores estén definidos en tailwind.config.js y que la clase esté correctamente escrita.

El dropdown no funciona

Asegúrate de que el componente Dropdown esté importado y que el template tenga la estructura correcta.

Los enlaces no navegan correctamente

Verifica que estés usando el componente Link de Inertia y no una etiqueta <a> normal.


Resumen

✅ Exploramos los componentes disponibles en Laravel Jetstream
✅ Usamos el componente Button con estilos personalizados
✅ Agregamos Call to Action en diferentes secciones
✅ Implementamos el menú desplegable de usuario
✅ Creamos un layout reutilizable para todo el sitio

¡Excelente trabajo! Ahora tienes un portafolio más completo con componentes interactivos y llamados a la acción estratégicos. En las siguientes clases profundizaremos en funcionalidades específicas como la visualización detallada de proyectos y la implementación del formulario de contacto.


Recursos adicionales


¿Tienes dudas sobre cómo usar los componentes de Jetstream? Recuerda revisar la documentación oficial o consultar los videos del curso para ver ejemplos prácticos.

Comentarios

Entradas más populares de este blog

Tareas con Laravel + Inertia + Jetstream

HACER CAMBIOS-AL EJEMPLO POR DEFECTO

Laravel + Inertia - ejemplo