Skip to main content
The Sugerencias Service manages customer suggestions and feedback submissions, including configurable suggestion types and read status tracking.

Overview

This service handles the complete lifecycle of customer suggestions: creation, retrieval, marking as read, and deletion. It also manages the configuration of suggestion types displayed in the dropdown.

Dependencies

  • sugerenciasRepository - Database operations for suggestions

Methods

getAll()

Retrieves all customer suggestions.
import { sugerenciasService } from '$lib/server/services/sugerencias.service.js';

const sugerencias = await sugerenciasService.getAll();
sugerencias
array
Array of suggestion objects:
  • id: Suggestion identifier
  • tipo: Type/category of suggestion
  • nombre: Customer name
  • mensaje: Suggestion message/content
  • leida: Boolean indicating if read
  • createdAt: Timestamp of creation

Example Response

[
  {
    id: 1,
    tipo: 'Mejora del Servicio',
    nombre: 'Juan Pérez',
    mensaje: 'Sugerencia para mejorar la atención...',
    leida: false,
    createdAt: '2024-03-12T10:30:00Z'
  },
  {
    id: 2,
    tipo: 'Producto Nuevo',
    nombre: 'María González',
    mensaje: 'Podrían agregar cemento especial...',
    leida: true,
    createdAt: '2024-03-11T15:20:00Z'
  }
]

Implementation Details

Source: src/lib/server/services/sugerencias.service.js:5-7

create()

Creates a new customer suggestion.
import { sugerenciasService } from '$lib/server/services/sugerencias.service.js';

await sugerenciasService.create({
  tipo: 'Mejora del Servicio',
  nombre: 'Carlos Rodríguez',
  mensaje: 'Sería genial si pudieran ofrecer entrega los sábados'
});
tipo
string
required
Type or category of suggestion (from configured options)
nombre
string
required
Customer name
mensaje
string
required
Suggestion message or feedback content
New suggestions are created with leida: false by default.

Implementation Details

Source: src/lib/server/services/sugerencias.service.js:10-12

markAsRead()

Marks a suggestion as read.
import { sugerenciasService } from '$lib/server/services/sugerencias.service.js';

await sugerenciasService.markAsRead(1);
id
number
required
Suggestion ID to mark as read
This sets leida: true for the specified suggestion.

Implementation Details

Source: src/lib/server/services/sugerencias.service.js:15-17

delete()

Deletes a suggestion by ID.
import { sugerenciasService } from '$lib/server/services/sugerencias.service.js';

await sugerenciasService.delete(1);
id
number
required
Suggestion ID to delete

Implementation Details

Source: src/lib/server/services/sugerencias.service.js:20-22

getConfig()

Retrieves the configuration for suggestion types.
import { sugerenciasService } from '$lib/server/services/sugerencias.service.js';

const config = await sugerenciasService.getConfig();
config
object
Configuration object containing:
  • opciones: Array of suggestion type strings

Example Response

{
  opciones: [
    'Mejora del Servicio',
    'Producto Nuevo',
    'Queja',
    'Felicitación',
    'Otro'
  ]
}

Implementation Details

Source: src/lib/server/services/sugerencias.service.js:25-27

updateConfig()

Updates the suggestion type options with JSON parsing.
import { sugerenciasService } from '$lib/server/services/sugerencias.service.js';

await sugerenciasService.updateConfig([
  'Mejora del Servicio',
  'Producto Nuevo',
  'Sucursal Nueva',
  'Queja',
  'Felicitación'
]);

// Or with JSON string
await sugerenciasService.updateConfig(
  JSON.stringify(['Opción 1', 'Opción 2'])
);
opciones
string | array
required
Array of suggestion type strings or JSON string

JSON Parsing

The service automatically handles both formats:
const parsed = typeof opciones === 'string' ? JSON.parse(opciones) : opciones;

Implementation Details

Source: src/lib/server/services/sugerencias.service.js:30-33

Usage Examples

Public Suggestion Form

// src/routes/sugerencias/+page.server.js
import { sugerenciasService } from '$lib/server/services/sugerencias.service.js';
import { fail } from '@sveltejs/kit';

export async function load() {
  const config = await sugerenciasService.getConfig();
  return { tiposSugerencia: config.opciones };
}

export const actions = {
  submit: async ({ request }) => {
    const formData = await request.formData();
    
    const tipo = formData.get('tipo');
    const nombre = formData.get('nombre');
    const mensaje = formData.get('mensaje');
    
    // Validation
    if (!tipo || !nombre || !mensaje) {
      return fail(400, { error: 'Todos los campos son requeridos' });
    }
    
    if (mensaje.length < 10) {
      return fail(400, { error: 'El mensaje debe tener al menos 10 caracteres' });
    }
    
    try {
      await sugerenciasService.create({ tipo, nombre, mensaje });
      return { success: true };
    } catch (error) {
      return fail(500, { error: 'Error al enviar sugerencia' });
    }
  }
};
<!-- src/routes/sugerencias/+page.svelte -->
<script>
  import { enhance } from '$app/forms';
  export let data;
  export let form;
</script>

<form method="POST" action="?/submit" use:enhance>
  <h1>Envíanos tu Sugerencia</h1>
  
  <div>
    <label for="nombre">Tu Nombre</label>
    <input
      type="text"
      id="nombre"
      name="nombre"
      required
    />
  </div>
  
  <div>
    <label for="tipo">Tipo de Sugerencia</label>
    <select id="tipo" name="tipo" required>
      <option value="">Selecciona un tipo</option>
      {#each data.tiposSugerencia as tipo}
        <option value="{tipo}">{tipo}</option>
      {/each}
    </select>
  </div>
  
  <div>
    <label for="mensaje">Tu Mensaje</label>
    <textarea
      id="mensaje"
      name="mensaje"
      rows="6"
      required
      minlength="10"
    ></textarea>
  </div>
  
  <button type="submit">Enviar Sugerencia</button>
  
  {#if form?.success}
    <p class="success">¡Gracias! Tu sugerencia ha sido enviada.</p>
  {/if}
  
  {#if form?.error}
    <p class="error">{form.error}</p>
  {/if}
</form>

Admin Dashboard

// src/routes/admin/sugerencias/+page.server.js
import { sugerenciasService } from '$lib/server/services/sugerencias.service.js';
import { fail } from '@sveltejs/kit';

export async function load() {
  const [sugerencias, config] = await Promise.all([
    sugerenciasService.getAll(),
    sugerenciasService.getConfig()
  ]);
  
  return { 
    sugerencias,
    tiposConfig: config.opciones 
  };
}

export const actions = {
  markRead: async ({ request }) => {
    const formData = await request.formData();
    const id = parseInt(formData.get('id'));
    
    try {
      await sugerenciasService.markAsRead(id);
      return { success: true };
    } catch (error) {
      return fail(500, { error: error.message });
    }
  },
  
  delete: async ({ request }) => {
    const formData = await request.formData();
    const id = parseInt(formData.get('id'));
    
    try {
      await sugerenciasService.delete(id);
      return { success: true };
    } catch (error) {
      return fail(500, { error: error.message });
    }
  },
  
  updateConfig: async ({ request }) => {
    const formData = await request.formData();
    const opciones = formData.get('opciones'); // JSON string
    
    try {
      await sugerenciasService.updateConfig(opciones);
      return { success: true };
    } catch (error) {
      return fail(500, { error: error.message });
    }
  }
};
<!-- src/routes/admin/sugerencias/+page.svelte -->
<script>
  export let data;
  
  $: unreadCount = data.sugerencias.filter(s => !s.leida).length;
</script>

<div class="admin-sugerencias">
  <header>
    <h1>Sugerencias de Clientes</h1>
    <span class="badge">{unreadCount} sin leer</span>
  </header>
  
  <div class="sugerencias-list">
    {#each data.sugerencias as sugerencia (sugerencia.id)}
      <div class="sugerencia" class:unread={!sugerencia.leida}>
        <div class="header">
          <span class="tipo">{sugerencia.tipo}</span>
          <span class="date">
            {new Date(sugerencia.createdAt).toLocaleDateString()}
          </span>
        </div>
        
        <h3>{sugerencia.nombre}</h3>
        <p>{sugerencia.mensaje}</p>
        
        <div class="actions">
          {#if !sugerencia.leida}
            <form method="POST" action="?/markRead">
              <input type="hidden" name="id" value="{sugerencia.id}" />
              <button type="submit">Marcar como Leída</button>
            </form>
          {/if}
          
          <form method="POST" action="?/delete">
            <input type="hidden" name="id" value="{sugerencia.id}" />
            <button type="submit" class="delete">Eliminar</button>
          </form>
        </div>
      </div>
    {/each}
  </div>
</div>

Data Structure

Sugerencia Object

interface Sugerencia {
  id: number;
  tipo: string;
  nombre: string;
  mensaje: string;
  leida: boolean;
  createdAt: string; // ISO timestamp
}

Config Object

interface SugerenciasConfig {
  opciones: string[];
}

Default Suggestion Types

Common suggestion type options:
const defaultOptions = [
  'Mejora del Servicio',
  'Producto Nuevo',
  'Queja',
  'Felicitación',
  'Sucursal Nueva',
  'Horario de Atención',
  'Otro'
];

await sugerenciasService.updateConfig(defaultOptions);

Filtering and Sorting

Get Unread Suggestions

import { sugerenciasService } from '$lib/server/services/sugerencias.service.js';

const allSugerencias = await sugerenciasService.getAll();
const unread = allSugerencias.filter(s => !s.leida);
const read = allSugerencias.filter(s => s.leida);

Sort by Date

const sorted = allSugerencias.sort((a, b) => 
  new Date(b.createdAt) - new Date(a.createdAt)
);

Filter by Type

const quejas = allSugerencias.filter(s => s.tipo === 'Queja');
const mejoras = allSugerencias.filter(s => s.tipo === 'Mejora del Servicio');

Best Practices

Display unread count prominently in admin dashboard for quick visibility.
Validate and sanitize user input before creating suggestions to prevent XSS attacks.
Limit suggestion types to 5-7 options for better user experience.
Consider implementing email notifications for new suggestions.

Email Notification Example

import { sugerenciasService } from '$lib/server/services/sugerencias.service.js';
import { sendEmail } from '$lib/server/email';

async function createSugerenciaWithNotification(data) {
  // Create suggestion
  await sugerenciasService.create(data);
  
  // Send notification email
  await sendEmail({
    to: 'admin@provesa.com',
    subject: `Nueva Sugerencia: ${data.tipo}`,
    html: `
      <h2>Nueva Sugerencia Recibida</h2>
      <p><strong>Tipo:</strong> ${data.tipo}</p>
      <p><strong>Nombre:</strong> ${data.nombre}</p>
      <p><strong>Mensaje:</strong></p>
      <p>${data.mensaje}</p>
    `
  });
}

Statistics and Analytics

import { sugerenciasService } from '$lib/server/services/sugerencias.service.js';

async function getSugerenciasStats() {
  const sugerencias = await sugerenciasService.getAll();
  
  const stats = {
    total: sugerencias.length,
    unread: sugerencias.filter(s => !s.leida).length,
    byType: {}
  };
  
  // Count by type
  sugerencias.forEach(s => {
    stats.byType[s.tipo] = (stats.byType[s.tipo] || 0) + 1;
  });
  
  // Most common type
  stats.mostCommon = Object.entries(stats.byType)
    .sort(([,a], [,b]) => b - a)[0]?.[0];
  
  return stats;
}

Validation Helpers

function validateSugerencia(data) {
  const errors = [];
  
  if (!data.tipo || data.tipo.trim().length === 0) {
    errors.push('Tipo es requerido');
  }
  
  if (!data.nombre || data.nombre.trim().length < 2) {
    errors.push('Nombre debe tener al menos 2 caracteres');
  }
  
  if (!data.mensaje || data.mensaje.trim().length < 10) {
    errors.push('Mensaje debe tener al menos 10 caracteres');
  }
  
  if (data.mensaje && data.mensaje.length > 1000) {
    errors.push('Mensaje no puede exceder 1000 caracteres');
  }
  
  return errors;
}

// Usage
const errors = validateSugerencia(formData);
if (errors.length > 0) {
  return fail(400, { errors });
}