15. Formulario para editar Chatbots

 

Formulario para Editar Chatbots

1. Creación del Componente ChatbotForm

1.1. Estructura del Componente

Crear resources/js/Components/Chatbots/ChatbotForm.vue:

vue

<script setup>

import { defineProps, computed } from 'vue'

import { useForm } from '@inertiajs/vue3'


const props = defineProps({

    form: {

        type: Object,

        required: true

    },

    isEditing: {

        type: Boolean,

        default: false

    }

})


// Modelos disponibles

const models = [

    { value: 'gpt-3.5-turbo', label: 'GPT-3.5 Turbo' },

    { value: 'gpt-4', label: 'GPT-4' },

    { value: 'gpt-4-turbo', label: 'GPT-4 Turbo' },

    { value: 'gpt-4o', label: 'GPT-4o' },

]

</script>


<template>

    <div class="grid grid-cols-6 gap-6">

        <!-- Nombre -->

        <div class="col-span-6 sm:col-span-4">

            <label 

                for="name" 

                class="block text-sm font-medium text-gray-700 dark:text-gray-300"

            >

                Nombre

            </label>

            <input

                id="name"

                v-model="form.name"

                type="text"

                class="mt-1 block w-full rounded-md border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-blue-500 focus:ring-blue-500"

                placeholder="Ej: Chef Ecuador, Asistente de Ventas..."

            />

            <

                v-if="form.errors.name" 

                class="mt-1 text-sm text-red-600 dark:text-red-400"

            >

                {{ form.errors.name }}

            </p>

        </div>


        <!-- System Prompt -->

        <div class="col-span-6 sm:col-span-4">

            <label 

                for="system_prompt" 

                class="block text-sm font-medium text-gray-700 dark:text-gray-300"

            >

                System Prompt

            </label>

            <textarea

                id="system_prompt"

                v-model="form.system_prompt"

                rows="6"

                class="mt-1 block w-full rounded-md border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-blue-500 focus:ring-blue-500"

                placeholder="Instrucciones que guiarán el comportamiento del chatbot..."

            />

            <

                v-if="form.errors.system_prompt" 

                class="mt-1 text-sm text-red-600 dark:text-red-400"

            >

                {{ form.errors.system_prompt }}

            </p>

        </div>


        <!-- Modelo -->

        <div class="col-span-6 sm:col-span-4">

            <label 

                for="model" 

                class="block text-sm font-medium text-gray-700 dark:text-gray-300"

            >

                Modelo de IA

            </label>

            <select

                id="model"

                v-model="form.model"

                class="mt-1 block w-full rounded-md border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-blue-500 focus:ring-blue-500"

            >

                <option 

                    v-for="model in models" 

                    :key="model.value"

                    :value="model.value"

                >

                    {{ model.label }}

                </option>

            </select>

            <

                v-if="form.errors.model" 

                class="mt-1 text-sm text-red-600 dark:text-red-400"

            >

                {{ form.errors.model }}

            </p>

        </div>


        <!-- Temperatura -->

        <div class="col-span-6 sm:col-span-4">

            <label 

                for="temperature" 

                class="block text-sm font-medium text-gray-700 dark:text-gray-300"

            >

                Temperatura: {{ form.temperature }}

            </label>

            <input

                id="temperature"

                v-model="form.temperature"

                type="range"

                min="0"

                max="1"

                step="0.1"

                class="mt-1 block w-full"

            />

            <div class="flex justify-between text-xs text-gray-500 dark:text-gray-400">

                <span>Preciso</span>

                <span>Creativo</span>

            </div>

            <

                v-if="form.errors.temperature" 

                class="mt-1 text-sm text-red-600 dark:text-red-400"

            >

                {{ form.errors.temperature }}

            </p>

        </div>

    </div>

</template>

1.2. Importar el Componente en la Vista Edit

En resources/js/Pages/Chatbots/Edit.vue:

vue

<script setup>

import { defineProps, ref } from 'vue'

import { Link, useForm } from '@inertiajs/vue3'

import AppLayout from '@/Layouts/AppLayout.vue'

import { ArrowLeftIcon } from '@heroicons/vue/24/solid'

import FormSection from '@/Components/FormSection.vue'

import PrimaryButton from '@/Components/PrimaryButton.vue'

import ActionMessage from '@/Components/ActionMessage.vue'

import ChatbotForm from '@/Components/Chatbots/ChatbotForm.vue' // 👈 Importar


const props = defineProps({

    chatbot: {

        type: Object,

        required: true

    }

})


// Formulario con los datos del chatbot

const form = useForm({

    name: props.chatbot.name,

    system_prompt: props.chatbot.system_prompt,

    model: props.chatbot.model,

    temperature: props.chatbot.temperature.toString(), // Convertir a string

})


const showSuccess = ref(false)


// Enviar formulario

const handleSubmit = () => {

    form.put(route('chatbots.update', props.chatbot.id), {

        preserveScroll: true,

        onSuccess: () => {

            showSuccess.value = true

            setTimeout(() => {

                showSuccess.value = false

            }, 3000)

        }

    })

}

</script>


<template>

    <AppLayout :title="`Editar: ${chatbot.name}`">

        <!-- Header -->

        <template #header>

            <div class="flex items-center gap-4">

                <Link 

                    :href="route('chatbots.show', chatbot.id)" 

                    class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors"

                    aria-label="Volver al detalle del chatbot"

                >

                    <ArrowLeftIcon class="w-6 h-6" />

                </Link>

                

                <h1 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">

                    Editar: {{ chatbot.name }}

                </h1>

            </div>

        </template>


        <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">

                        <!-- Formulario -->

                        <FormSection @submitted="handleSubmit">

                            <template #title>

                                Información del Chatbot

                            </template>


                            <template #description>

                                Actualiza la información de tu chatbot de inteligencia artificial.

                            </template>


                            <template #form>

                                <!-- 👇 Usar el componente ChatbotForm -->

                                <ChatbotForm 

                                    :form="form"

                                    :is-editing="true"

                                />

                            </template>


                            <template #actions>

                                <div class="flex items-center gap-4">

                                    <PrimaryButton 

                                        :disabled="form.processing"

                                        :class="{

                                            'cursor-not-allowed opacity-50': form.processing

                                        }"

                                    >

                                        {{ form.processing ? 'Guardando...' : 'Guardar Cambios' }}

                                    </PrimaryButton>


                                    <ActionMessage :on="showSuccess" class="mr-2">

                                        <span class="text-green-600 dark:text-green-400">

                                            ✓ Chatbot actualizado correctamente

                                        </span>

                                    </ActionMessage>


                                    <Link 

                                        :href="route('chatbots.show', chatbot.id)"

                                        class="text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-200"

                                    >

                                        Cancelar

                                    </Link>

                                </div>

                            </template>

                        </FormSection>

                    </div>

                </div>

            </div>

        </div>

    </AppLayout>

</template>


2. Problema de Autenticación (CSRF)

2.1. ¿Por qué ocurre el error "Esta acción no está autorizada"?

Causa: Laravel requiere un token CSRF para todas las peticiones POST, PUT, PATCH y DELETE.

Solución 1: Usar @csrf en formularios Blade

blade

<form method="POST">

    @csrf

    <!-- ... -->

</form>

Solución 2: Inertia maneja automáticamente el CSRF

Inertia.js incluye automáticamente el token CSRF en las peticiones.

2.2. Verificar Configuración de Sanctum

En config/sanctum.php:

php

'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(

    '%s%s',

    'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',

    env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''

))),

En app/Http/Kernel.php:

php

protected $middlewareGroups = [

    'web' => [

        // ... otros middleware

        \App\Http\Middleware\VerifyCsrfToken::class, // Verificar que esté presente

    ],

];


3. Implementación Completa del Formulario

3.1. Mejoras en el Componente ChatbotForm

Agregar vista previa del System Prompt:

vue

<script setup>

import { ref, computed } from 'vue'

import { EyeIcon, EyeSlashIcon } from '@heroicons/vue/24/solid'


// ... props y configuraciones


const showPreview = ref(false)


const togglePreview = () => {

    showPreview.value = !showPreview.value

}

</script>


<template>

    <!-- System Prompt con vista previa -->

    <div class="col-span-6 sm:col-span-4">

        <div class="flex justify-between items-center">

            <label 

                for="system_prompt" 

                class="block text-sm font-medium text-gray-700 dark:text-gray-300"

            >

                System Prompt

            </label>

            <button 

                type="button"

                @click="togglePreview"

                class="text-sm text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300"

            >

                <EyeIcon v-if="!showPreview" class="w-4 h-4 inline-block" />

                <EyeSlashIcon v-else class="w-4 h-4 inline-block" />

                {{ showPreview ? 'Ocultar' : 'Vista previa' }}

            </button>

        </div>

        

        <textarea

            id="system_prompt"

            v-model="form.system_prompt"

            rows="6"

            class="mt-1 block w-full rounded-md border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-blue-500 focus:ring-blue-500 font-mono text-sm"

            placeholder="Instrucciones que guiarán el comportamiento del chatbot..."

        />

        

        <!-- Vista previa -->

        <div 

            v-if="showPreview"

            class="mt-2 p-4 bg-gray-50 dark:bg-gray-900 rounded-md border border-gray-200 dark:border-gray-700 whitespace-pre-wrap text-sm"

        >

            {{ form.system_prompt || 'El prompt aparecerá aquí...' }}

        </div>

        

        <

            v-if="form.errors.system_prompt" 

            class="mt-1 text-sm text-red-600 dark:text-red-400"

        >

            {{ form.errors.system_prompt }}

        </p>

    </div>

</template>

3.2. Validación en Tiempo Real

Agregar validaciones en el componente padre:

vue

<script setup>

import { computed } from 'vue'

import { useForm } from '@inertiajs/vue3'


// ... código existente


const form = useForm({

    name: props.chatbot.name,

    system_prompt: props.chatbot.system_prompt,

    model: props.chatbot.model,

    temperature: props.chatbot.temperature.toString(),

})


// Validación en tiempo real

const isValid = computed(() => {

    return form.name.length > 0 && 

           form.name.length <= 255 &&

           form.system_prompt.length >= 10 &&

           form.system_prompt.length <= 5000 &&

           form.model.length > 0 &&

           parseFloat(form.temperature) >= 0 &&

           parseFloat(form.temperature) <= 1

})


// Mensajes de validación personalizados

const validationMessages = computed(() => {

    const messages = []

    

    if (form.name.length === 0) {

        messages.push('El nombre es obligatorio')

    } else if (form.name.length > 255) {

        messages.push('El nombre no puede exceder 255 caracteres')

    }

    

    if (form.system_prompt.length < 10) {

        messages.push('El system prompt debe tener al menos 10 caracteres')

    } else if (form.system_prompt.length > 5000) {

        messages.push('El system prompt no puede exceder 5000 caracteres')

    }

    

    const temp = parseFloat(form.temperature)

    if (isNaN(temp) || temp < 0 || temp > 1) {

        messages.push('La temperatura debe estar entre 0 y 1')

    }

    

    return messages

})

</script>


<template>

    <FormSection @submitted="handleSubmit">

        <!-- ... -->

        

        <template #actions>

            <div class="flex items-center gap-4">

                <PrimaryButton 

                    :disabled="!isValid || form.processing"

                    :class="{

                        'cursor-not-allowed opacity-50': !isValid || form.processing

                    }"

                >

                    {{ form.processing ? 'Guardando...' : 'Guardar Cambios' }}

                </PrimaryButton>


                <!-- Mostrar errores de validación -->

                <div v-if="validationMessages.length > 0" class="text-sm text-red-600 dark:text-red-400">

                    <p v-for="(msg, index) in validationMessages" :key="index">

                        • {{ msg }}

                    </p>

                </div>


                <ActionMessage :on="showSuccess" class="mr-2">

                    <span class="text-green-600 dark:text-green-400">

                        ✓ Chatbot actualizado correctamente

                    </span>

                </ActionMessage>


                <Link 

                    :href="route('chatbots.show', chatbot.id)"

                    class="text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-200"

                >

                    Cancelar

                </Link>

            </div>

        </template>

    </FormSection>

</template>


4. Manejo de Errores

4.1. Mostrar Errores del Servidor

En Edit.vue:

vue

<script setup>

import { ref } from 'vue'

import { useForm } from '@inertiajs/vue3'

import { ExclamationCircleIcon } from '@heroicons/vue/24/solid'


// ... código existente


const serverErrors = ref([])


const handleSubmit = () => {

    form.put(route('chatbots.update', props.chatbot.id), {

        preserveScroll: true,

        onSuccess: () => {

            showSuccess.value = true

            serverErrors.value = []

            setTimeout(() => {

                showSuccess.value = false

            }, 3000)

        },

        onError: (errors) => {

            // Convertir errores en array de mensajes

            serverErrors.value = Object.values(errors).flat()

        }

    })

}

</script>


<template>

    <FormSection @submitted="handleSubmit">

        <!-- Mostrar errores del servidor -->

        <div v-if="serverErrors.length > 0" class="col-span-6">

            <div class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-md p-4">

                <div class="flex items-start gap-3">

                    <ExclamationCircleIcon class="w-5 h-5 text-red-600 dark:text-red-400 flex-shrink-0 mt-0.5" />

                    <div>

                        <h4 class="text-sm font-medium text-red-800 dark:text-red-200">

                            Error al guardar los cambios

                        </h4>

                        <ul class="mt-1 text-sm text-red-700 dark:text-red-300 list-disc list-inside">

                            <li v-for="(error, index) in serverErrors" :key="index">

                                {{ error }}

                            </li>

                        </ul>

                    </div>

                </div>

            </div>

        </div>

        

        <!-- ... resto del formulario -->

    </FormSection>

</template>


5. Resumen

5.1. Archivos Creados/Modificados

Archivo

Acción

resources/js/Components/Chatbots/ChatbotForm.vue

✅ Creado

resources/js/Pages/Chatbots/Edit.vue

✅ Actualizado

5.2. Funcionalidades Implementadas

  • ✅ Componente reutilizable para formularios

  • ✅ Vista previa del system prompt

  • ✅ Validaciones en tiempo real

  • ✅ Manejo de errores del servidor

  • ✅ Mensajes de éxito y error

  • ✅ Control deslizante para temperatura

  • ✅ Select para modelos de IA

5.3. Comandos Útiles

bash

# Verificar rutas

php artisan route:list


# Limpiar caché de rutas

php artisan route:clear

¡El formulario de edición está completamente funcional! ✏️


Comentarios

Entradas más populares de este blog

1. Introducción al curso de Chatbots personalizados con Laravel & OpenAI

2. Prerequisitos para el Curso de Chatbots con Laravel y OpenAI

4. Herramientas de Calidad