Skip to main content

Overview

The SugerenciasForm component provides an interactive form for visitors to submit suggestions, service requests, complaints, or compliments. Features a custom-styled dropdown selector, form validation, and success confirmation with auto-reset.

Location

src/lib/components/SugerenciasForm.svelte

Props

tipos
array
Array of suggestion types available in the dropdown menu

Features

  • Custom Dropdown: Styled dropdown menu (not native <select>)
  • Progressive Enhancement: Uses SvelteKit’s use:enhance for smooth form submission
  • Auto-reset: Form clears automatically 3 seconds after successful submission
  • Success Animation: Displays checkmark confirmation when submission succeeds
  • Character Counter: Shows message length out of 500 characters
  • Optional Name Field: Anonymous submissions allowed
  • Form Validation: Submit button disabled when message is empty
  • Responsive Design: Fully responsive with mobile-first approach

Form Fields

tipo
string
required
Type of message selected from dropdown (hidden input)Default options:
  • Sugerencia de Servicio
  • Nuevo Producto Requerido
  • Reclamo
  • Felicitación
nombre
string
User’s name (optional, defaults to “Anónimo”)
mensaje
string
required
The suggestion/feedback message (required, max 500 characters)

Usage Example

Basic Usage (Default Types)

+page.svelte
<script>
  import SugerenciasForm from '$lib/components/SugerenciasForm.svelte';
</script>

<SugerenciasForm />

With Custom Types

+page.svelte
<script>
  import SugerenciasForm from '$lib/components/SugerenciasForm.svelte';

  const customTypes = [
    'Sugerencia General',
    'Mejora de Servicio',
    'Producto Nuevo',
    'Queja',
    'Elogio',
    'Consulta'
  ];
</script>

<SugerenciasForm tipos={customTypes} />

With Database Types

From /src/routes/+page.server.js:19:
+page.server.js
export async function load() {
  const sugOptions = await sugerenciasService.getTipos();
  
  return {
    sugOptions
  };
}
+page.svelte
<script>
  import SugerenciasForm from '$lib/components/SugerenciasForm.svelte';
  
  let { data } = $props();
</script>

<SugerenciasForm tipos={data.sugOptions} />

Form Actions

The component expects a form action at ?/enviarSugerencia in the parent route:
+page.server.js
export const actions = {
  enviarSugerencia: async ({ request }) => {
    const data = await request.formData();
    const tipo = data.get('tipo');
    const nombre = data.get('nombre') || 'Anónimo';
    const mensaje = data.get('mensaje');

    await sugerenciasService.create({ tipo, nombre, mensaje });

    return { success: true };
  }
};

States

The form has two main states:

1. Input State (Default)

Shows the form with:
  • Custom dropdown for message type
  • Optional name input
  • Required message textarea
  • Character counter
  • Submit button (disabled if message empty)

2. Success State

After successful submission:
  • Green checkmark icon
  • Success message: “¡Mensaje Enviado!”
  • Thank you text
  • Auto-resets to input state after 3 seconds

Custom Dropdown Behavior

The dropdown component:
  1. Displays current selection
  2. Shows chevron icon (rotates 180° when open)
  3. Opens overlay with all options
  4. Highlights selected option with dot indicator
  5. Closes on option select or outside click
<!-- Custom Dropdown Structure -->
<button type="button" onclick={() => dropdownOpen = !dropdownOpen}>
  {tipo}
  <ChevronDown class={dropdownOpen ? 'rotate-180' : ''} />
</button>

{#if dropdownOpen}
  <div class="overlay" onclick={close}></div>
  <div class="dropdown-menu">
    {#each tipos as t}
      <button onclick={() => selectTipo(t)}>
        {t}
      </button>
    {/each}
  </div>
{/if}

Progressive Enhancement

Uses SvelteKit’s use:enhance for seamless form submission:
use:enhance={() => {
  isSubmitting = true;
  return async ({ update, result }) => {
    isSubmitting = false;
    if (result.type === 'success') {
      enviado = true;
      setTimeout(() => {
        enviado = false;
        nombre = '';
        mensaje = '';
      }, 3000);
    }
    await update({ reset: false });
  };
}}
Benefits:
  • No full page reload
  • Optimistic UI updates
  • Graceful fallback without JavaScript
  • Custom success handling

Visual Structure

Input State

┌─────────────────────────────────────────────────────────┐
│           [Icon] Buzón de Sugerencias                   │
│                ─── ─ ─                                  │
│     Su opinión es vital para nuestro crecimiento        │
│                                                          │
│  ┌──────────────────────┬──────────────────────┐       │
│  │ [▼] Tipo de mensaje  │ [👤] Nombre (Opcional)│       │
│  └──────────────────────┴──────────────────────┘       │
│                                                          │
│  ┌─────────────────────────────────────────────┐       │
│  │ [💬] Su mensaje                              │       │
│  │                                              │       │
│  │ Escriba aquí sus comentarios...              │       │
│  │                                              │       │
│  └─────────────────────────────────────────────┘       │
│                                         0/500           │
│                                                          │
│            [ Enviar Sugerencia → ]                      │
│                                                          │
│           🔒 Su mensaje es confidencial                 │
└─────────────────────────────────────────────────────────┘

Success State

┌─────────────────────────────────────────────────────────┐
│                                                          │
│                      ┌───┐                              │
│                      │ ✓ │                              │
│                      └───┘                              │
│                                                          │
│               ¡Mensaje Enviado!                         │
│                                                          │
│    Gracias por compartir su opinión.                    │
│         La revisaremos pronto.                          │
│                                                          │
└─────────────────────────────────────────────────────────┘

Styling Details

  • Section Background: White with decorative gradient blobs
  • Form Card: Soft gray background (bg-soft-gray) with border
  • Inputs: White background with slate-200 borders
  • Focus State: Primary color border with ring effect
  • Submit Button: Primary color with shadow and hover scale
  • Success Card: Green tinted background with green border

Form Validation

  • Submit button disabled when:
    • Message field is empty
    • Message contains only whitespace
  • Character limit: 500 characters (displayed below textarea)

Accessibility

  • Semantic HTML form structure
  • Label associations with for and id attributes
  • Icon labels for visual context
  • Disabled button has cursor-not-allowed
  • Focus states with ring effects

Dependencies

  • $app/forms - SvelteKit’s enhance function
  • lucide-svelte - Icons (MessageSquareText, Send, User, ChevronDown)