13. Detalle de un Chatbot
Detalle de un Chatbot
1. Configuración de la Ruta de Detalle
1.1. Actualizar el Botón "Ver" en el Listado
En ChatbotListItem.vue:
vue
<script setup>
import { EyeIcon, PencilIcon, TrashIcon } from '@heroicons/vue/24/solid'
import { PrimaryButton, SecondaryButton, DangerButton } from '@/Components/Buttons'
const emit = defineEmits(['delete'])
defineProps({
chatbot: {
type: Object,
required: true
}
})
</script>
<template>
<div class="flex flex-col md:flex-row md:items-center md:justify-between py-4">
<div class="flex-1">
<div class="text-gray-800 dark:text-gray-200 font-medium">
{{ chatbot.name }}
</div>
<div class="text-sm text-gray-500 dark:text-gray-400">
Creado: {{ dayjs(chatbot.created_at).fromNow() }}
</div>
</div>
<div class="flex space-x-2 mt-4 md:mt-0">
<!-- 👇 Ruta corregida para el detalle -->
<PrimaryButton
:href="route('chatbots.show', chatbot.id)"
aria-label="Ver detalles del chatbot"
class="gap-2"
>
<EyeIcon class="w-4 h-4" />
<span>Ver</span>
</PrimaryButton>
<SecondaryButton
:href="route('chatbots.edit', chatbot.id)"
aria-label="Editar chatbot"
class="gap-2"
>
<PencilIcon class="w-4 h-4" />
<span>Editar</span>
</SecondaryButton>
<DangerButton
aria-label="Eliminar chatbot"
class="gap-2"
@click="$emit('delete', chatbot.id)"
>
<TrashIcon class="w-4 h-4" />
<span>Eliminar</span>
</DangerButton>
</div>
</div>
</template>
2. Controlador - Método Show
2.1. Actualizar ChatbotController
En app/Http/Controllers/ChatbotController.php:
php
<?php
namespace App\Http\Controllers;
use App\Models\Chatbot;
use Illuminate\Http\Request;
use Inertia\Inertia;
class ChatbotController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$chatbots = $request->user()->chatbots;
return Inertia::render('Chatbots/Index', [
'chatbots' => $chatbots,
]);
}
/**
* Display the specified resource.
*/
public function show(Request $request, Chatbot $chatbot)
{
// Verificar que el chatbot pertenece al usuario
if ($chatbot->user_id !== $request->user()->id) {
abort(403, 'No tienes permiso para ver este chatbot.');
}
return Inertia::render('Chatbots/Show', [
'chatbot' => $chatbot,
]);
}
// ... otros métodos
}
3. Creación de la Vista Show
3.1. Estructura del Componente
Crear resources/js/Pages/Chatbots/Show.vue:
vue
<script setup>
import { defineProps } from 'vue'
import { Link } from '@inertiajs/vue3'
import AppLayout from '@/Layouts/AppLayout.vue'
import dayjs from '@/utils/dayjs'
import PrimaryButton from '@/Components/PrimaryButton.vue'
import SecondaryButton from '@/Components/SecondaryButton.vue'
import {
ArrowLeftIcon,
PencilIcon,
TrashIcon,
ChatBubbleLeftRightIcon,
} from '@heroicons/vue/24/solid'
defineProps({
chatbot: {
type: Object,
required: true
}
})
</script>
<template>
<AppLayout :title="`Chatbot: ${chatbot.name}`">
<!-- Header -->
<template #header>
<div class="flex items-center gap-4">
<!-- Botón de volver -->
<Link
:href="route('chatbots.index')"
class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors"
aria-label="Volver al listado de chatbots"
>
<ArrowLeftIcon class="w-6 h-6" />
</Link>
<h1 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ chatbot.name }}
</h1>
</div>
</template>
<!-- Contenido principal -->
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg">
<div class="p-6 lg:p-8">
<!-- Grid de información -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Columna izquierda -->
<div class="space-y-6">
<!-- System Prompt -->
<div>
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">
System Prompt
</h3>
<div class="mt-2 p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
<p class="text-gray-800 dark:text-gray-200 whitespace-pre-wrap">
{{ chatbot.system_prompt }}
</p>
</div>
</div>
<!-- Temperatura -->
<div>
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">
Temperatura
</h3>
<div class="mt-2 flex items-center gap-2">
<div class="flex-1 h-2 bg-gray-200 dark:bg-gray-700 rounded-full">
<div
class="h-2 bg-blue-600 rounded-full transition-all"
:style="{ width: `${chatbot.temperature * 100}%` }"
/>
</div>
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">
{{ chatbot.temperature }}
</span>
</div>
</div>
<!-- Modelo -->
<div>
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">
Modelo de IA
</h3>
<div class="mt-2">
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200">
{{ chatbot.model }}
</span>
</div>
</div>
<!-- Fecha de creación -->
<div>
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">
Creado
</h3>
<div class="mt-2 text-gray-800 dark:text-gray-200">
{{ dayjs(chatbot.created_at).format('DD/MM/YYYY HH:mm') }}
<span class="text-sm text-gray-500 dark:text-gray-400">
({{ dayjs(chatbot.created_at).fromNow() }})
</span>
</div>
</div>
</div>
<!-- Columna derecha - Acciones y estadísticas -->
<div class="space-y-6">
<!-- Acciones rápidas -->
<div>
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">
Acciones
</h3>
<div class="mt-4 space-y-3">
<!-- Chatear -->
<SecondaryButton
:href="route('chats.create', chatbot.id)"
class="w-full justify-center gap-2"
aria-label="Iniciar chat con este chatbot"
>
<ChatBubbleLeftRightIcon class="w-5 h-5" />
<span>Chatear</span>
</SecondaryButton>
<!-- Editar -->
<SecondaryButton
:href="route('chatbots.edit', chatbot.id)"
class="w-full justify-center gap-2"
aria-label="Editar chatbot"
>
<PencilIcon class="w-5 h-5" />
<span>Editar</span>
</SecondaryButton>
<!-- Eliminar -->
<DangerButton
class="w-full justify-center gap-2"
aria-label="Eliminar chatbot"
>
<TrashIcon class="w-5 h-5" />
<span>Eliminar</span>
</DangerButton>
</div>
</div>
<!-- Estadísticas -->
<div>
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">
Estadísticas
</h3>
<div class="mt-4 grid grid-cols-2 gap-4">
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 text-center">
<div class="text-2xl font-bold text-gray-800 dark:text-gray-200">
0
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
Conversaciones
</div>
</div>
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 text-center">
<div class="text-2xl font-bold text-gray-800 dark:text-gray-200">
0
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
Mensajes
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</AppLayout>
</template>
4. Mejoras y Funcionalidades Adicionales
4.1. Verificar Propiedad del Chatbot
En el controlador, agregar política de autorización:
php
// En app/Policies/ChatbotPolicy.php (crear si no existe)
public function view(User $user, Chatbot $chatbot)
{
return $user->id === $chatbot->user_id;
}
En el controlador usando policies:
php
public function show(Request $request, Chatbot $chatbot)
{
$this->authorize('view', $chatbot);
return Inertia::render('Chatbots/Show', [
'chatbot' => $chatbot,
]);
}
4.2. Componente de Estadísticas
Crear resources/js/Components/Chatbots/ChatbotStats.vue:
vue
<script setup>
import { computed } from 'vue'
const props = defineProps({
chatbot: {
type: Object,
required: true
}
})
// Esto se actualizará cuando tengamos el sistema de chats
const stats = computed(() => ({
conversations: 0,
messages: 0,
lastActivity: null,
}))
</script>
<template>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 text-center">
<div class="text-2xl font-bold text-gray-800 dark:text-gray-200">
{{ stats.conversations }}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
Conversaciones
</div>
</div>
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 text-center">
<div class="text-2xl font-bold text-gray-800 dark:text-gray-200">
{{ stats.messages }}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
Mensajes
</div>
</div>
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 text-center col-span-2">
<div class="text-sm text-gray-800 dark:text-gray-200">
{{ stats.lastActivity || 'Sin actividad' }}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
Última actividad
</div>
</div>
</div>
</template>
4.3. Mejorar el Formateo de Fechas
En utils/dayjs.js, agregar más formatos:
javascript
// ... configuraciones existentes
// Funciones de formato adicionales
export const formatDateLong = (date) => {
return dayjs(date).format('dddd, D [de] MMMM [de] YYYY')
}
export const formatDateTime = (date) => {
return dayjs(date).format('DD/MM/YYYY HH:mm:ss')
}
export const formatTimeAgo = (date) => {
return dayjs(date).fromNow()
}
Uso en el componente:
vue
<script setup>
import { formatDateLong, formatTimeAgo } from '@/utils/dayjs'
</script>
<template>
<div>
<div class="text-gray-800 dark:text-gray-200">
{{ formatDateLong(chatbot.created_at) }}
</div>
<div class="text-sm text-gray-500 dark:text-gray-400">
{{ formatTimeAgo(chatbot.created_at) }}
</div>
</div>
</template>
5. Navegación y Breadcrumbs
5.1. Componente de Breadcrumbs
Crear resources/js/Components/Breadcrumbs.vue:
vue
<script setup>
import { Link } from '@inertiajs/vue3'
import { ChevronRightIcon } from '@heroicons/vue/24/solid'
defineProps({
items: {
type: Array,
required: true
}
})
</script>
<template>
<nav class="flex" aria-label="Breadcrumb">
<ol class="flex items-center space-x-2">
<li v-for="(item, index) in items" :key="index">
<div class="flex items-center">
<ChevronRightIcon
v-if="index > 0"
class="w-4 h-4 text-gray-400 dark:text-gray-600 mr-2"
aria-hidden="true"
/>
<Link
v-if="item.href"
:href="item.href"
class="text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
>
{{ item.label }}
</Link>
<span v-else class="text-sm font-medium text-gray-800 dark:text-gray-200">
{{ item.label }}
</span>
</div>
</li>
</ol>
</nav>
</template>
Uso en Show.vue:
vue
<template>
<AppLayout :title="`Chatbot: ${chatbot.name}`">
<template #header>
<div class="flex flex-col gap-2">
<Breadcrumbs :items="[
{ label: 'Chatbots', href: route('chatbots.index') },
{ label: chatbot.name }
]" />
<h1 class="font-semibold text-xl text-gray-800 dark:text-gray-200">
{{ chatbot.name }}
</h1>
</div>
</template>
<!-- ... -->
</AppLayout>
</template>
6. Resumen
6.1. Archivos Creados/Modificados
6.2. Estructura de la Vista Show
text
Show.vue
├── AppLayout
│ ├── Header
│ │ ├── Botón Volver
│ │ └── Título del Chatbot
│ └── Contenido
│ ├── Columna Izquierda
│ │ ├── System Prompt
│ │ ├── Temperatura (barra)
│ │ ├── Modelo de IA
│ │ └── Fecha de Creación
│ └── Columna Derecha
│ ├── Acciones
│ │ ├── Chatear
│ │ ├── Editar
│ │ └── Eliminar
│ └── Estadísticas
│ ├── Conversaciones
│ └── Mensajes
6.3. Funcionalidades Implementadas
✅ Visualización de todos los datos del chatbot
✅ Barra de temperatura con representación visual
✅ Botón de volver al listado
✅ Acciones rápidas desde el detalle
✅ Autorización para verificar propiedad
✅ Formato de fechas legible
✅ Soporte Dark Mode
¡La vista de detalle está completa y lista para usar! 🚀
Comentarios
Publicar un comentario