Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ashcroft08/provesa-web/llms.txt

Use this file to discover all available pages before exploring further.

The Empleo Service manages employment branch locations (sucursales) that are displayed in job application forms.

Overview

This simple service manages a list of branch locations where candidates can apply for jobs. Branches can be activated or deactivated to control which locations appear in the public job application form.

Dependencies

  • empleoRepository - Database operations for employment branches

Methods

getAll()

Retrieves all employment branches, ordered alphabetically by name.
import { empleoService } from '$lib/server/services/empleo.service.js';

const sucursales = await empleoService.getAll();
sucursales
array
Array of branch objects:
  • id: Branch identifier (number)
  • nombre: Branch name
  • activa: Boolean indicating if branch is active

Example Response

[
  {
    id: 1,
    nombre: 'Asunción Centro',
    activa: true
  },
  {
    id: 2,
    nombre: 'Ciudad del Este',
    activa: true
  },
  {
    id: 3,
    nombre: 'Encarnación',
    activa: false
  }
]

Implementation Details

Source: src/lib/server/services/empleo.service.js:5-7 Branches are ordered alphabetically by nombre field.

getActive()

Retrieves only active employment branches.
import { empleoService } from '$lib/server/services/empleo.service.js';

const activeSucursales = await empleoService.getActive();
activeSucursales
array
Array of active branch objects (same structure as getAll)
Use this method for public-facing job application forms to show only available branches.

Example Response

[
  {
    id: 1,
    nombre: 'Asunción Centro',
    activa: true
  },
  {
    id: 2,
    nombre: 'Ciudad del Este',
    activa: true
  }
]

Implementation Details

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

addSucursal()

Creates a new employment branch.
import { empleoService } from '$lib/server/services/empleo.service.js';

await empleoService.addSucursal('Fernando de la Mora');
nombre
string
required
Branch name
New branches are created as inactive (activa: false) by default.

Implementation Details

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

toggleSucursal()

Activates or deactivates an employment branch.
import { empleoService } from '$lib/server/services/empleo.service.js';

// Activate branch
await empleoService.toggleSucursal(3, true);

// Deactivate branch
await empleoService.toggleSucursal(3, false);
id
number
required
Branch ID to toggle
activa
boolean
required
New active status (true = active, false = inactive)

Implementation Details

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

deleteSucursal()

Deletes an employment branch by ID.
import { empleoService } from '$lib/server/services/empleo.service.js';

await empleoService.deleteSucursal(3);
id
number
required
Branch ID to delete
Deleting a branch does not delete associated job applications. Consider deactivating instead.

Implementation Details

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

Usage Examples

Public Job Application Form

// src/routes/empleo/+page.server.js
import { empleoService } from '$lib/server/services/empleo.service.js';

export async function load() {
  const sucursales = await empleoService.getActive();
  return { sucursales };
}
<!-- src/routes/empleo/+page.svelte -->
<script>
  export let data;
</script>

<form method="POST" action="?/apply">
  <h1>Trabaja con Nosotros</h1>
  
  <div>
    <label for="sucursal">Sucursal de Interés *</label>
    <select id="sucursal" name="sucursal" required>
      <option value="">Selecciona una sucursal</option>
      {#each data.sucursales as sucursal}
        <option value="{sucursal.nombre}">{sucursal.nombre}</option>
      {/each}
    </select>
  </div>
  
  <!-- Other form fields -->
  
  <button type="submit">Enviar Postulación</button>
</form>

Admin Branch Management

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

export async function load() {
  const sucursales = await empleoService.getAll();
  return { sucursales };
}

export const actions = {
  add: async ({ request }) => {
    const formData = await request.formData();
    const nombre = formData.get('nombre');
    
    if (!nombre || nombre.trim().length < 2) {
      return fail(400, { error: 'Nombre debe tener al menos 2 caracteres' });
    }
    
    try {
      await empleoService.addSucursal(nombre.trim());
      return { success: true };
    } catch (error) {
      console.error('Error adding branch:', error);
      return fail(500, { error: 'Error al agregar sucursal' });
    }
  },
  
  toggle: async ({ request }) => {
    const formData = await request.formData();
    const id = parseInt(formData.get('id'));
    const activa = formData.get('activa') === 'true';
    
    try {
      await empleoService.toggleSucursal(id, activa);
      return { success: true };
    } catch (error) {
      console.error('Error toggling branch:', error);
      return fail(500, { error: 'Error al actualizar sucursal' });
    }
  },
  
  delete: async ({ request }) => {
    const formData = await request.formData();
    const id = parseInt(formData.get('id'));
    
    try {
      await empleoService.deleteSucursal(id);
      return { success: true };
    } catch (error) {
      console.error('Error deleting branch:', error);
      return fail(500, { error: 'Error al eliminar sucursal' });
    }
  }
};
<!-- src/routes/admin/empleo/+page.svelte -->
<script>
  import { enhance } from '$app/forms';
  export let data;
  
  $: activeCount = data.sucursales.filter(s => s.activa).length;
  $: inactiveCount = data.sucursales.length - activeCount;
</script>

<div class="admin-empleo">
  <header>
    <h1>Sucursales de Empleo</h1>
    <div class="stats">
      <span class="badge active">{activeCount} activas</span>
      <span class="badge inactive">{inactiveCount} inactivas</span>
    </div>
  </header>
  
  <!-- Add new branch form -->
  <form method="POST" action="?/add" class="add-form" use:enhance>
    <input
      type="text"
      name="nombre"
      placeholder="Nombre de nueva sucursal"
      required
    />
    <button type="submit">Agregar</button>
  </form>
  
  <!-- Branch list -->
  <div class="sucursales-list">
    {#each data.sucursales as sucursal (sucursal.id)}
      <div class="sucursal-item" class:inactive={!sucursal.activa}>
        <div class="info">
          <h3>{sucursal.nombre}</h3>
          <span class="status">
            {sucursal.activa ? 'Activa' : 'Inactiva'}
          </span>
        </div>
        
        <div class="actions">
          <form method="POST" action="?/toggle" use:enhance>
            <input type="hidden" name="id" value="{sucursal.id}" />
            <input type="hidden" name="activa" value="{!sucursal.activa}" />
            <button type="submit" class="toggle">
              {sucursal.activa ? 'Desactivar' : 'Activar'}
            </button>
          </form>
          
          <form method="POST" action="?/delete" use:enhance>
            <input type="hidden" name="id" value="{sucursal.id}" />
            <button type="submit" class="delete">Eliminar</button>
          </form>
        </div>
      </div>
    {/each}
    
    {#if data.sucursales.length === 0}
      <p class="empty">No hay sucursales configuradas</p>
    {/if}
  </div>
</div>

Integration with Postulaciones Service

// src/routes/empleo/+page.server.js
import { empleoService } from '$lib/server/services/empleo.service.js';
import { postulacionesService } from '$lib/server/services/postulaciones.service.js';
import { fail } from '@sveltejs/kit';

export async function load() {
  const sucursales = await empleoService.getActive();
  return { sucursales };
}

export const actions = {
  apply: async ({ request }) => {
    const formData = await request.formData();
    
    const nombre = formData.get('nombre');
    const telefono = formData.get('telefono');
    const email = formData.get('email');
    const sucursal = formData.get('sucursal');
    const mensaje = formData.get('mensaje');
    const cvFile = formData.get('cv');
    
    // Validate sucursal exists and is active
    const activeSucursales = await empleoService.getActive();
    const isValidSucursal = activeSucursales.some(s => s.nombre === sucursal);
    
    if (!isValidSucursal) {
      return fail(400, { error: 'Sucursal inválida o no disponible' });
    }
    
    // Create application
    try {
      await postulacionesService.create(
        { nombre, telefono, email, sucursal, mensaje },
        cvFile
      );
      return { success: true };
    } catch (error) {
      console.error('Error creating application:', error);
      return fail(500, { error: 'Error al enviar postulación' });
    }
  }
};

Data Structure

Sucursal Object

interface EmpleoSucursal {
  id: number;
  nombre: string;
  activa: boolean;
}

Validation Helpers

function validateSucursalName(nombre) {
  const errors = [];
  
  if (!nombre || nombre.trim().length < 2) {
    errors.push('Nombre debe tener al menos 2 caracteres');
  }
  
  if (nombre.length > 100) {
    errors.push('Nombre no puede exceder 100 caracteres');
  }
  
  // Check for special characters
  const validPattern = /^[a-zA-ZáéíóúÁÉÍÓÚñÑ\s-]+$/;
  if (!validPattern.test(nombre)) {
    errors.push('Nombre contiene caracteres inválidos');
  }
  
  return errors;
}

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

Check for Duplicate Names

import { empleoService } from '$lib/server/services/empleo.service.js';

async function isDuplicateSucursal(nombre) {
  const sucursales = await empleoService.getAll();
  return sucursales.some(s => 
    s.nombre.toLowerCase().trim() === nombre.toLowerCase().trim()
  );
}

// Usage in action
export const actions = {
  add: async ({ request }) => {
    const formData = await request.formData();
    const nombre = formData.get('nombre').trim();
    
    if (await isDuplicateSucursal(nombre)) {
      return fail(400, { error: 'Ya existe una sucursal con ese nombre' });
    }
    
    await empleoService.addSucursal(nombre);
    return { success: true };
  }
};

Statistics and Analytics

import { empleoService } from '$lib/server/services/empleo.service.js';
import { postulacionesService } from '$lib/server/services/postulaciones.service.js';

async function getSucursalesStats() {
  const [sucursales, postulaciones] = await Promise.all([
    empleoService.getAll(),
    postulacionesService.getAll()
  ]);
  
  const stats = sucursales.map(sucursal => {
    const applications = postulaciones.filter(p => 
      p.sucursal === sucursal.nombre
    );
    
    return {
      id: sucursal.id,
      nombre: sucursal.nombre,
      activa: sucursal.activa,
      totalApplications: applications.length,
      withCV: applications.filter(p => p.cvUrl).length,
      recentApplications: applications.filter(p => {
        const daysSince = (Date.now() - new Date(p.createdAt)) / (1000 * 60 * 60 * 24);
        return daysSince <= 30;
      }).length
    };
  });
  
  return stats;
}

Usage in Admin Dashboard

// src/routes/admin/empleo/stats/+page.server.js
import { empleoService } from '$lib/server/services/empleo.service.js';
import { postulacionesService } from '$lib/server/services/postulaciones.service.js';

export async function load() {
  const [sucursales, postulaciones] = await Promise.all([
    empleoService.getAll(),
    postulacionesService.getAll()
  ]);
  
  const stats = sucursales.map(sucursal => ({
    ...sucursal,
    applications: postulaciones.filter(p => p.sucursal === sucursal.nombre).length
  }));
  
  return { stats };
}
<!-- Stats display -->
<script>
  export let data;
  
  $: sortedStats = [...data.stats].sort((a, b) => 
    b.applications - a.applications
  );
</script>

<div class="stats-grid">
  {#each sortedStats as stat}
    <div class="stat-card">
      <h3>{stat.nombre}</h3>
      <div class="metric">
        <span class="value">{stat.applications}</span>
        <span class="label">postulaciones</span>
      </div>
      <div class="status" class:active={stat.activa}>
        {stat.activa ? 'Activa' : 'Inactiva'}
      </div>
    </div>
  {/each}
</div>

Best Practices

Use descriptive branch names that match physical locations (e.g., “Asunción Centro” instead of just “Asunción”).
Always validate that selected branches are active before creating job applications.
Consider deactivating branches instead of deleting them to preserve historical data.
Keep branch names consistent with your postulaciones data for accurate reporting.

Bulk Operations

import { empleoService } from '$lib/server/services/empleo.service.js';

/**
 * Activate multiple branches at once
 */
async function activateMultipleSucursales(ids) {
  const promises = ids.map(id => 
    empleoService.toggleSucursal(id, true)
  );
  await Promise.all(promises);
}

/**
 * Deactivate all branches except specified ones
 */
async function deactivateExcept(exceptIds) {
  const sucursales = await empleoService.getAll();
  const toDeactivate = sucursales
    .filter(s => !exceptIds.includes(s.id) && s.activa)
    .map(s => empleoService.toggleSucursal(s.id, false));
  
  await Promise.all(toDeactivate);
}

/**
 * Seed initial branches
 */
async function seedSucursales() {
  const branches = [
    'Asunción Centro',
    'Ciudad del Este',
    'Encarnación',
    'Pedro Juan Caballero',
    'Concepción'
  ];
  
  for (const nombre of branches) {
    await empleoService.addSucursal(nombre);
  }
  
  console.log(`Created ${branches.length} branches`);
}

Database Schema

CREATE TABLE empleo_sucursales (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  nombre TEXT NOT NULL UNIQUE,
  activa BOOLEAN DEFAULT FALSE
);

CREATE INDEX idx_empleo_nombre ON empleo_sucursales(nombre);
CREATE INDEX idx_empleo_activa ON empleo_sucursales(activa);

Migration Example

// migrate-empleo-data.js
import { empleoService } from '$lib/server/services/empleo.service.js';
import { postulacionesService } from '$lib/server/services/postulaciones.service.js';

/**
 * Migrate existing postulaciones to use standardized branch names
 */
async function migrateSucursalNames() {
  const postulaciones = await postulacionesService.getAll();
  
  // Map old names to new names
  const mapping = {
    'asuncion': 'Asunción Centro',
    'cde': 'Ciudad del Este',
    'encarnacion': 'Encarnación'
  };
  
  for (const postulacion of postulaciones) {
    const oldName = postulacion.sucursal.toLowerCase();
    const newName = mapping[oldName];
    
    if (newName && newName !== postulacion.sucursal) {
      // Update postulacion with new name
      console.log(`Updating ${postulacion.id}: ${oldName}${newName}`);
      // Implementation depends on your update logic
    }
  }
}

Build docs developers (and LLMs) love