Skip to main content

Overview

The Cashouts system is a comprehensive workflow management tool for registering, reviewing, and tracking cashout operations across multiple gaming companies. It features real-time queue management, statistics tracking, and Google Sheets synchronization. Access: /cashouts/ (requires authentication)

System Architecture

Main Features

Submit Cashouts

Operators can submit cashout requests with operation codes and optional observations

Review Queue

Supervisors review pending cashouts with real-time timer tracking

Statistics

Comprehensive analytics by operator, company, shift, and time period

Leaderboard

Real-time rankings of operator performance

Company Rules

Searchable database of cashout procedures per company

Team Management

Supervisors can create and manage user accounts
The sidebar provides access to all system features (cashouts/index.html:22-66):
<aside class="sidebar">
  <button class="role-btn operator active">Submit a Cashout</button>
  <button class="role-btn supervisor">🔍 Review a Cashout</button>
  <button class="role-btn rules">📜 Cashout Rules</button>
  <button class="role-btn leaderboard">🏆 Leaderboard</button>
  <button class="role-btn stats">📊 Stats</button>
  <button class="role-btn" id="manageTeamBtn" style="display: none;">👥 Manage Team</button>
</aside>
The “Manage Team” button is only visible to users with the supervisor role (cashouts/index.html:611-615)

Submit a Cashout

Form Fields

1

Operation Code

Unique identifier for the cashout transaction
<input type="text" id="operationCode" name="operationCode" required>
2

Operator Name

Auto-filled and read-only - populated from authenticated user
operatorNameInput.value = currentUser.fullName;
operatorNameInput.readOnly = true; // Prevents impersonation
3

Company

Dropdown populated from company database
<select id="company" name="company" required>
  <option value="">Seleccione una compañía</option>
</select>
4

Observation (Optional)

Modal prompt after form submission
<div id="observacionModal" class="modal-overlay">
  <h3>¿Tienes alguna observación sobre este cashout?</h3>
  <div class="modal-buttons">
    <button id="btnObservacionSi" class="btn-si"></button>
    <button id="btnObservacionNo" class="btn-no">NO</button>
  </div>
</div>

Submission Flow

// 1. Form submission captures data
cashoutPendiente = {
  operationCode: document.getElementById('operationCode').value,
  operatorName: currentUser.fullName, // From JWT
  company: document.getElementById('company').value,
  observacion: ""
};

// 2. Optional observation modal
document.getElementById('observacionModal').style.display = 'flex';

// 3. Submit to API
await apiFetch(`/cashouts`, {
  method: "POST",
  body: JSON.stringify(cashoutPendiente)
});

// 4. Success notification
document.getElementById('operatorSuccess').style.display = 'block';
showNotification("¡Cash out enviado correctamente!", "success");

Review Queue

Real-Time Queue Management

The review queue auto-refreshes every 10 seconds when active (cashouts/index.html:780-792):
function iniciarAutoRefresh() {
  if (autoRefreshInterval) return;
  autoRefreshInterval = setInterval(() => {
    if (reviewSection.style.display === 'block') cargarCola();
  }, 10000); // Refresh every 10 seconds
}

Queue Item Display

Each pending cashout shows:
  • Operation Code (bold, prominent)
  • Company Name
  • Operator Name
  • Start Time (cashoutChecking timestamp)
  • Live Timer (⏱️ elapsed time)
  • Observation Tag (if present)

Review Process

1

Select Action

Reviewer clicks Approve or Reject button
2

Reviewer Identification

Modal prompts for reviewer name (first time):
<select id="revisionTurnoSelect">
  <option value="">Seleccione un revisor</option>
  <option value="Melanie Barrientos">Melanie Barrientos</option>
  <option value="Cristopher Cardozo">Cristopher Cardozo</option>
  <!-- ...more reviewers... -->
</select>
3

Observation Review

If operator left an observation, reviewer can add comments:
<div id="observacionOriginalDisplay">
  <strong>Observación Operador:</strong><br>${observacionOriginal}
</div>
<textarea id="supervisorObsTexto" placeholder="Escribe tu complemento aquí..."></textarea>
4

Submit Decision

Action is processed via API and reflected in statistics

Cashout Rules

Searchable database of company-specific cashout procedures (cashouts/index.html:171-193):

Features

Search

<input type="text" id="searchRules" 
       placeholder="🔍 Buscar compañía por nombre..." 
       style="width: 100%;">

Add Company

Supervisors can add new companies (hidden by default):
<div id="newCompanyContainer" style="display: none;">
  <input type="text" id="newCompanyName" 
         placeholder="Nombre de la nueva compañía">
  <button id="btnCreateCompany">Guardar</button>
</div>

Grid Layout

Rules displayed in responsive grid:
<div id="rulesGrid" class="rules-grid"></div>

Database Sync

Rules loaded from backend on demand:
<div id="rulesLoading" style="text-align: center;">
  ⏳ Cargando reglas desde la base de datos...
</div>

Statistics Dashboard

Comprehensive analytics modal (cashouts/index.html:196-432):

Filters

<div class="stats-filters">
  <input type="date" id="statsStartDate" />
  <input type="date" id="statsEndDate" />
  <select id="statsCompanyFilter">
    <option value="">Todas las compañías</option>
  </select>
  <button id="applyStatsFilters">Aplicar Filtros</button>
</div>

Metrics Overview

Approved Cashouts

Green card with total approved count
<div id="totalApproved" style="color: #27ae60;">-</div>

Rejected Cashouts

Red card with total rejected count
<div id="totalRejected" style="color: #e74c3c;">-</div>

Approval Rate

Blue card with percentage
<div id="approvalRate" style="color: #3498db;">-</div>

Total Processed

Orange card with total count
<div id="totalProcessed" style="color: #f39c12;">-</div>

Detailed Reports

Table ranking by operator performance:
#OperatorApprovedRejectedTotalRate
1John Doe2451225795.3%
<table id="rankingTable">
  <thead>
    <tr>
      <th>#</th>
      <th>Operador</th>
      <th>Aprobados</th>
      <th>Rechazados</th>
      <th>Total</th>
      <th>Tasa</th>
    </tr>
  </thead>
  <tbody id="rankingTableBody"></tbody>
</table>

Leaderboard

Real-time operator rankings modal (cashouts/index.html:530-541):
<div id="leaderboardModal" class="modal-overlay">
  <div class="modal-content leaderboard-modal">
    <div class="leaderboard-header">
      <h2 class="leaderboard-title">🏆 LEADERBOARD</h2>
      <button id="cerrarLeaderboard" class="btn-close-leaderboard"></button>
    </div>
    <div id="leaderboardContent" class="leaderboard-body">
      <p>Cargando estadísticas...</p>
    </div>
  </div>
</div>

Team Management

Available to: Supervisors only

Create New Users

<div style="background: rgba(42, 157, 143, 0.05);">
  <h3>+ Crear Nuevo Usuario</h3>
  <input type="text" id="newFullName" placeholder="Nombre Completo">
  <input type="text" id="newUsername" placeholder="Usuario de acceso">
  <input type="password" id="newPassword" placeholder="Contraseña">
  <select id="newRole">
    <option value="analista">Analista</option>
    <option value="supervisor">Supervisor</option>
    <option value="chats">Chats</option>
  </select>
  <button id="btnCreateUser">Crear Cuenta</button>
</div>

User List Table

<table>
  <thead>
    <tr>
      <th>Nombre</th>
      <th>Usuario</th>
      <th>Rol</th>
      <th>Acción</th>
    </tr>
  </thead>
  <tbody id="usersTableBody">
    <!-- Dynamically populated -->
  </tbody>
</table>

API Endpoints

EndpointMethodPurposeAuth Required
/api/cashoutsPOSTSubmit new cashout
/api/cashouts?status=pending&limit=300GETFetch pending queue
/api/cashoutsGETFetch all cashouts (filtered)
Base URL: https://general-cashouts.onrender.com/api

Queue Rendering Logic

Smart Update Strategy

To prevent UI flicker, the queue only re-renders when actual changes occur:
function renderizarCola(items) {
  // Sort by newest first
  items.sort((a, b) => 
    new Date(b.cashoutChecking || b.timestamp) - 
    new Date(a.cashoutChecking || a.timestamp)
  );

  const idsActuales = items.map(i => i.row).sort().join(',');
  const idsEnUI = Array.from(verificationQueue.querySelectorAll('[data-cashout-id]'))
    .map(el => el.getAttribute('data-cashout-id'))
    .sort().join(',');

  // Only update timers if no structural changes
  if (idsActuales === idsEnUI && items.length > 0) {
    items.forEach(item => {
      const div = verificationQueue.querySelector(`[data-cashout-id="${item.row}"]`);
      if (div && item.cashoutChecking) {
        const cro = div.querySelector('.cronometro-live');
        if (cro) cro.textContent = `⏱️ ${calcularTiempoTranscurrido(item.cashoutChecking)}`;
      }
    });
    return; // Skip full re-render
  }

  // Full render if items changed
  // ...
}

Notifications System

function showNotification(msg, type = "success") {
  notification.textContent = msg;
  notification.style.background = type === "error" 
    ? "linear-gradient(135deg, #FF6B9D, #FFA07A)" 
    : "linear-gradient(135deg, #00C9FF, #92FE9D)";
  notification.style.color = type === "error" ? "#fff" : "#003d5c";
  notification.classList.add('show');
  setTimeout(() => notification.classList.remove('show'), 3200);
}
Usage examples:
  • showNotification("¡Cash out enviado correctamente!", "success")
  • showNotification("Error al subir cash out.", "error")

Best Practices

Use the observation field to document unusual circumstances, helping reviewers make informed decisions.
Keep the review queue tab active to benefit from auto-refresh and real-time updates.
Apply date and company filters to generate targeted reports for specific periods or partners.
Queue items with long elapsed times may indicate stalled reviews that need attention.

Build docs developers (and LLMs) love