PHP WebShell

Текущая директория: /var/www/bitcardoApp/backyard/user/orders

Просмотр файла: process_order.php

<?php
// backyard/user/orders/process_order.php
include '../common/header.php';

if (!isset($conn)) {
    include_once '../../config/db_config.php';
}
require_once '../../models/dashboard/index.php';

date_default_timezone_set('Africa/Lagos');

// Absolute base for card images (from your working link)
$IMG_BASE = 'https://wallet.bitcardo.com/backyard/uploads/cards/';

// --------------------------------- helpers ---------------------------------
function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
function fmt_money($n, $dec=2){ return number_format((float)$n, $dec); }
function status_badge_class($status_raw){
    $s = strtoupper(trim((string)$status_raw));
    if (in_array($s, ['SUCCESS','COMPLETED','APPROVED'])) return 'bg-success';
    if (in_array($s, ['PENDING','PROCESSING'])) return 'bg-warning';
    if (in_array($s, ['DECLINED','FAILED','REJECTED'])) return 'bg-danger';
    if ($s === 'MIXED') return 'bg-info';
    return 'bg-secondary';
}
function safe_now(mysqli $conn){
    $r = mysqli_query($conn, "SELECT NOW() as n");
    $row = $r? mysqli_fetch_assoc($r):null;
    if($r) mysqli_free_result($r);
    return $row? $row['n'] : date('Y-m-d H:i:s');
}

/**
 * Save uploaded evidence file for a trade into 'uploads/cards/<year>/<month>/<batch>/'
 * Returns array on success or false on failure
 */
function save_evidence_upload(mysqli $conn, int $trade_id, string $batch_ref, string $file_field='evidence'){
    if (!isset($_FILES[$file_field]) || !is_uploaded_file($_FILES[$file_field]['tmp_name'])) return false;
    $f = $_FILES[$file_field];
    if ($f['error'] !== UPLOAD_ERR_OK) return false;

    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mime = finfo_file($finfo, $f['tmp_name']);
    finfo_close($finfo);
    if (!preg_match('#^image/(jpeg|png|gif|webp)$#', $mime)) return false;

    // max 5MB
    if ($f['size'] > 5 * 1024 * 1024) return false;

    $year = date('Y');
    $month = date('m');
    $safeBatch = preg_replace('/[^A-Za-z0-9_-]/','_', $batch_ref ?: 'unknown');

    // <-- corrected to year/month/batch
    $baseDirRel = "uploads/cards/{$year}/{$month}/{$safeBatch}/";
    $absBase = __DIR__ . '/../../' . $baseDirRel;
    if (!is_dir($absBase)) {
        if (!mkdir($absBase, 0755, true) && !is_dir($absBase)) {
            return false;
        }
    }

    $ext = pathinfo($f['name'], PATHINFO_EXTENSION);
    $filename = strtoupper(bin2hex(random_bytes(6))) . ($ext ? ".{$ext}" : '');
    $destAbs = $absBase . $filename;
    // store relative path under uploads/cards/ (so other code that prefixes IMG_BASE still works)
    $destRel = $year . '/' . $month . '/' . $safeBatch . '/' . $filename;

    if (!move_uploaded_file($f['tmp_name'], $destAbs)) {
        return false;
    }

    $trade_id_i = (int)$trade_id;
    $path_safe = mysqli_real_escape_string($conn, $destRel);
    $orig_safe = mysqli_real_escape_string($conn, $f['name']);
    $mime_safe = mysqli_real_escape_string($conn, $mime);
    $size_i = (int)$f['size'];

    $q = "INSERT INTO card_trade_images (trade_id, path, original_name, mime, size, uploaded_at)
          VALUES ({$trade_id_i}, '{$path_safe}', '{$orig_safe}', '{$mime_safe}', {$size_i}, NOW())";
    if (!mysqli_query($conn, $q)) {
        @unlink($destAbs);
        return false;
    }

    return [
        'path' => $destRel,
        'original_name' => $f['name'],
        'mime' => $mime,
        'size' => $size_i,
    ];
}

// ---------------------------- POST: approve/decline -------------------------
// Uses card_ref (not trade_ref)
$flash = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'], $_POST['ref'])) {
    $action   = strtolower(trim($_POST['action']));
    $cardRef  = trim($_POST['ref']); // <- card_ref
    $ref_safe = mysqli_real_escape_string($conn, $cardRef);

    // Fetch card by card_ref
    $sql = "SELECT trade_id, user_id, trade_status, est_payout_ngn, batch_ref 
            FROM card_trade WHERE card_ref = '{$ref_safe}' LIMIT 1";
    $res = mysqli_query($conn, $sql);
    $card = $res ? mysqli_fetch_assoc($res) : null;
    if ($res) mysqli_free_result($res);

    if (!$card) {
        $flash = ['type'=>'danger','msg'=>"Card not found for card_ref: ".h($cardRef)];
    } else {
        $trade_id = (int)$card['trade_id'];
        $user_id  = (int)$card['user_id'];
        $status   = strtoupper(trim($card['trade_status'] ?? ''));
        $payout   = (float)($card['est_payout_ngn'] ?? 0);
        $batch_ref = $card['batch_ref'] ?? '';

        mysqli_begin_transaction($conn);
        try {
          if ($action === 'approve') {
            if (!in_array($status, ['APPROVED','SUCCESS','COMPLETED'])) {
                $now = safe_now($conn);
        
                // 1) Update card status
                $q1 = "UPDATE card_trade 
                       SET trade_status='APPROVED', trade_completed='{$now}' 
                       WHERE trade_id={$trade_id} LIMIT 1";
                if (!mysqli_query($conn, $q1)) { 
                    throw new Exception('Failed to update card status'); 
                }
        
                // 2) Find or create user's NGN fiat wallet, and ensure it has a wallet_add
                $q2 = "SELECT wallet_id, wallet_add, balance 
                       FROM user_wallets 
                       WHERE user_id={$user_id} AND coin='NGN' AND type='fiat' 
                       LIMIT 1";
                $r2 = mysqli_query($conn, $q2);
                $wallet = $r2 ? mysqli_fetch_assoc($r2) : null;
                if ($r2) mysqli_free_result($r2);
        
                $wallet_id = null;
                $wallet_address = '';
        
                if (!$wallet) {
                    // No NGN wallet yet: generate a wallet_add and create it
                    $wallet_address = 'NGN-' . $user_id . '-' . strtoupper(bin2hex(random_bytes(4)));
                    $wallet_address_safe = mysqli_real_escape_string($conn, $wallet_address);
        
                    $q2a = "INSERT INTO user_wallets (
                                cwallet_id, user_id, wallet_add, bank_name, wallet_qr, 
                                coin, icon, balance, type, label, wallet_status, created_at, updated_at
                            ) VALUES (
                                NULL, {$user_id}, '{$wallet_address_safe}', NULL, NULL,
                                'NGN', NULL, 0.0000000000, 'fiat', 'Naira Wallet', 'Active', NOW(), NOW()
                            )";
                    if (!mysqli_query($conn, $q2a)) { 
                        throw new Exception('Failed to create NGN wallet'); 
                    }
                    $wallet_id = (int)mysqli_insert_id($conn);
                } else {
                    // Existing wallet
                    $wallet_id = (int)$wallet['wallet_id'];
                    $wallet_address = (string)($wallet['wallet_add'] ?? '');
        
                    // If wallet_add is empty for some reason, generate one and store it
                    if ($wallet_address === '') {
                        $wallet_address = 'NGN-' . $user_id . '-' . strtoupper(bin2hex(random_bytes(4)));
                        $wallet_address_safe = mysqli_real_escape_string($conn, $wallet_address);
        
                        $q2u = "UPDATE user_wallets 
                                SET wallet_add='{$wallet_address_safe}', updated_at = NOW()
                                WHERE wallet_id = {$wallet_id} 
                                LIMIT 1";
                        if (!mysqli_query($conn, $q2u)) {
                            throw new Exception('Failed to update NGN wallet address');
                        }
                    }
                }
        
                // 3) Credit wallet
                $payout_sql = number_format($payout, 10, '.', '');
                $q3 = "UPDATE user_wallets 
                       SET balance = balance + {$payout_sql}, updated_at = NOW() 
                       WHERE wallet_id = {$wallet_id} 
                       LIMIT 1";
                if (!mysqli_query($conn, $q3)) { 
                    throw new Exception('Failed to credit wallet'); 
                }
        
                // 4) Log transaction with REAL receiver_address = wallet_add
                $wallet_id_safe       = mysqli_real_escape_string($conn, (string)$wallet_id);
                $wallet_address_safe2 = mysqli_real_escape_string($conn, (string)$wallet_address);
                $cardRef_safe         = mysqli_real_escape_string($conn, $cardRef);
        
                $q4 = sprintf(
                    "INSERT INTO transactions (
                        coin, user_id, wallet_id, transfer_id, sender_address, receiver_address, 
                        amount, type, txid, reference, provider, provider_meta, confirmation, 
                        status, applied, swap_id, note, updated_at, created_at
                    ) VALUES (
                        'NGN', %d, '%s', NULL, 'internal', '%s',
                        %s, 'giftcard_payout', NULL, '%s', 'system', NULL, 1,
                        'completed', 1, NULL, 'Gift card payout for %s', NOW(), NOW()
                    )",
                    $user_id,
                    $wallet_id_safe,
                    $wallet_address_safe2,
                    $payout_sql,
                    $cardRef_safe,
                    $cardRef_safe
                );
                if (!mysqli_query($conn, $q4)) { 
                    throw new Exception('Failed to log transaction'); 
                }
            }
        
            mysqli_commit($conn);
            $flash = [
                'type' => 'success',
                'msg'  => "Card ".h($cardRef)." approved and wallet credited (₦".fmt_money($payout).")."
            ];
        
            } elseif ($action === 'decline') {
                if (!in_array($status, ['DECLINED','FAILED','REJECTED'])) {
                    $q1 = "UPDATE card_trade SET trade_status='DECLINED', trade_completed=NOW() WHERE trade_id={$trade_id} LIMIT 1";
                    if (!mysqli_query($conn, $q1)) { throw new Exception('Failed to decline card'); }
                }

                // If evidence uploaded (only possible from single view), save it.
                $saved = save_evidence_upload($conn, $trade_id, $batch_ref, 'evidence');
                if ($saved === false && isset($_FILES['evidence']) && $_FILES['evidence']['error'] !== UPLOAD_ERR_NO_FILE) {
                    throw new Exception('Failed to save evidence upload (invalid file or server error)');
                }

                mysqli_commit($conn);
                $flash = ['type'=>'warning','msg'=>"Card ".h($cardRef)." declined." . ($saved ? ' Evidence saved.' : '')];
            } else {
                mysqli_rollback($conn);
                $flash = ['type'=>'danger','msg'=>'Unknown action.'];
            }
        } catch (Throwable $e) {
            mysqli_rollback($conn);
            $flash = ['type'=>'danger','msg'=>"Action failed: ".$e->getMessage()];
        }
    }
}

// ------------------------------ inputs for view -----------------------------
// In single view (?ref=), we now treat 'ref' as card_ref
$batch   = isset($_GET['batch']) ? trim($_GET['batch']) : '';
$cardRef = isset($_GET['ref'])   ? trim($_GET['ref'])   : '';
?>
<div class="nk-content nk-content-fluid">
  <div class="container-xl wide-lg">
    <div class="nk-content-body">

      <div class="nk-block-head">
        <div class="nk-block-between-md g-4">
          <div class="nk-block-head-content">
            <h4 class="nk-block-title fw-normal">Process Gift Card <?= $batch ? 'Batch' : 'Order'; ?></h4>
            <div class="nk-block-des">
              <p class="text-soft mb-1">Review <?= $batch ? 'all cards in this batch' : 'order details and attachments'; ?>.</p>
              <a href="../dashboard/index.php" class="btn btn-sm btn-outline-secondary">&larr; Back to Dashboard</a>
            </div>
          </div>
          <div class="nk-block-head-content">
            <ul class="nk-block-tools gx-3">
              <li><button class="btn btn-success" disabled><span>Approve</span></button></li>
              <li><button class="btn btn-warning" disabled><span>Mark Processing</span></button></li>
              <li><button class="btn btn-danger" disabled><span>Decline</span></button></li>
            </ul>
          </div>
        </div>
      </div>

      <?php if ($flash): ?>
        <div class="alert alert-<?= h($flash['type']); ?>"><?= h($flash['msg']); ?></div>
      <?php endif; ?>

<?php
// -------------------------------- BATCH VIEW --------------------------------
if ($batch !== '') {
    $details = dash_get_batch_details($conn, $batch);
    $b = $details['batch'];

    if (!$b) {
        echo '<div class="alert alert-warning">No batch found for: <strong>'.h($batch).'</strong></div>';
    } else {
        $full_name = trim(($b['first_name'] ?? '').' '.($b['last_name'] ?? ''));
        $status = dash_batch_status($b);
        $badge = status_badge_class($status);
        $created = $b['last_time'] ? date('M j, Y g:i A', strtotime($b['last_time'])) : '—';

        // fetch images for all cards at once
        $trade_ids = array_map(fn($c)=> (int)$c['trade_id'], $details['cards']);

        // fetch card_ref for all trade_ids in one shot (in case model didn't include it)
        $card_ref_map = [];
        if (!empty($trade_ids)) {
            $idlist = implode(',', array_map('intval', $trade_ids));
            $q = "SELECT trade_id, card_ref FROM card_trade WHERE trade_id IN ({$idlist})";
            if ($qr = mysqli_query($conn, $q)) {
                while ($r = mysqli_fetch_assoc($qr)) {
                    $card_ref_map[(int)$r['trade_id']] = $r['card_ref'];
                }
                mysqli_free_result($qr);
            }
        }

        $images_by_trade = dash_get_images_for_trades($conn, $trade_ids);
?>
      <div class="nk-block">
        <div class="row g-gs">

          <!-- Batch Summary -->
          <div class="col-12">
            <div class="card card-bordered">
              <div class="card-inner">
                <div class="d-flex justify-content-between align-items-center mb-2">
                  <h5 class="title mb-0">Batch #<?= h($batch); ?></h5>
                  <span class="badge <?= $badge; ?> px-3 py-2"><?= h($status); ?></span>
                </div>

                <div class="row gy-2">
                  <div class="col-md-3"><div class="small text-soft">Customer</div><div class="fw-bold"><?= h($full_name ?: '—'); ?></div></div>
                  <div class="col-md-3"><div class="small text-soft">Cards</div><div class="fw-bold"><?= (int)$b['cards_count']; ?></div></div>
                  <div class="col-md-3"><div class="small text-soft">Est. Payout (₦)</div><div class="fw-bold"><?= fmt_money($b['total_payout'], 2); ?></div></div>
                  <div class="col-md-3"><div class="small text-soft">Last Update</div><div class="fw-bold"><?= h($created); ?></div></div>

                  <div class="col-12 mt-2 small text-soft">
                    Status breakdown:
                    <span class="badge bg-success">OK: <?= (int)$b['ok_count']; ?></span>
                    <span class="badge bg-warning">Pending: <?= (int)$b['pending_count']; ?></span>
                    <span class="badge bg-danger">Declined: <?= (int)$b['bad_count']; ?></span>
                  </div>
                </div>
              </div>
            </div>
          </div>

          <!-- Cards in Batch -->
          <div class="col-12">
            <div class="card card-bordered">
              <div class="card-inner">
                <h6 class="title mb-3">Cards in this Batch</h6>

                <?php if (!empty($details['cards'])): ?>
                  <div class="row">
                  <?php foreach ($details['cards'] as $card):
                      $tid     = (int)$card['trade_id'];
                      $cardRefEach = $card_ref_map[$tid] ?? ''; // <- ensure we have card_ref
                      $brand   = $card['card_brand'] ?: '—';
                      $demon   = $card['demon'] ?: '—';
                      $amount  = fmt_money($card['card_value'] ?? 0, 2);
                      $curr    = $card['card_curr'] ?: '';
                      $payout  = fmt_money($card['est_payout_ngn'] ?? 0, 2);
                      $status  = strtoupper($card['trade_status'] ?: '');
                      $badgeC  = status_badge_class($status);
                      $imgs    = $images_by_trade[$tid] ?? [];
                      // only show action buttons when not already approved/declined
                      $canAct = !in_array($status, ['APPROVED','DECLINED','SUCCESS','COMPLETED','FAILED','REJECTED']);
                  ?>
                    <div class="col-12">
                      <div class="border rounded-4 p-3 mb-3">
                        <div class="row align-items-center g-2">
                          <div class="col-md-2">
                            <div class="small text-soft">Card Ref</div>
                            <div class="fw-bold">
                              <?php if ($cardRefEach !== ''): ?>
                                <a href="?ref=<?= urlencode($cardRefEach); ?>"><?= h($cardRefEach); ?></a>
                              <?php else: ?>
                                —
                              <?php endif; ?>
                            </div>
                          </div>
                          <div class="col-md-2">
                            <div class="small text-soft">Brand</div>
                            <div class="fw-bold"><?= h($brand); ?></div>
                          </div>
                          <div class="col-md-2">
                            <div class="small text-soft">Denom</div>
                            <div class="fw-bold"><?= h($demon); ?></div>
                          </div>
                          <div class="col-md-2">
                            <div class="small text-soft">Value</div>
                            <div class="fw-bold"><?= $amount; ?> <span class="currency"><?= h($curr); ?></span></div>
                          </div>
                          <div class="col-md-2">
                            <div class="small text-soft">Payout (₦)</div>
                            <div class="fw-bold"><?= $payout; ?></div>
                          </div>
                          <div class="col-md-1">
                            <div class="small text-soft">Status</div>
                            <span class="badge <?= $badgeC; ?>"><?= h(ucfirst(strtolower($status))); ?></span>
                          </div>

                          <?php if (!empty($imgs)): ?>
                          <!-- aligned + ABSOLUTE image path -->
                          <div class="col-md-1 col-12 d-flex flex-column justify-content-center">
                            <div class="small text-soft d-none d-md-block mb-1">Attachments</div>
                            <div class="d-flex flex-wrap gap-1">
                              <?php
                                // show only the FIRST attachment as "View" on batch row
                                $firstImg = $imgs[0] ?? null;
                                if ($firstImg):
                                    $p = $firstImg['path'] ?? '';
                                    $imgSrc = (preg_match('#^https?://#i', $p) ? $p : $IMG_BASE . ltrim($p, '/'));
                              ?>
                                <button type="button"
                                        class="btn btn-sm btn-outline-info view-card-btn mb-2 mb-md-0 py-0 text-center"
                                        data-bs-toggle="modal"
                                        data-bs-target="#imgModal"
                                        data-img="<?= h($imgSrc); ?>"
                                        data-ref="<?= h($cardRefEach); ?>"
                                        data-can-act="<?= $canAct ? '1' : '0' ?>">
                                  View
                                </button>
                              <?php endif; ?>
                            </div>
                          </div>
                          <?php endif; ?>

                        </div><!-- .row -->

                        <?php if ($canAct): ?>
                        <div class="mt-3 d-flex gap-2">
                          <!-- Approve form (batch view – immediate POST) -->
                          <form method="post" class="d-inline">
                            <input type="hidden" name="ref" value="<?= h($cardRefEach); ?>">
                            <input type="hidden" name="action" value="approve">
                            <button type="submit" class="btn btn-success btn-sm">Approve</button>
                          </form>

                          <!-- Decline in batch view: immediate POST WITHOUT evidence -->
                          <form method="post" class="d-inline" onsubmit="return confirm('Decline this card? This cannot be undone.');">
                            <input type="hidden" name="ref" value="<?= h($cardRefEach); ?>">
                            <input type="hidden" name="action" value="decline">
                            <button type="submit" class="btn btn-danger btn-sm">Decline</button>
                          </form>
                        </div>
                        <?php endif; ?>

                      </div>
                    </div>
                  <?php endforeach; ?>
                  </div>
                <?php else: ?>
                  <div class="alert alert-light border mb-0">No cards found in this batch.</div>
                <?php endif; ?>

              </div>
            </div>
          </div>

        </div>
      </div>
<?php
    }
// ------------------------------ SINGLE ORDER --------------------------------
} elseif ($cardRef !== '') {
    // ... (single view code unchanged except it still shows all attachments)
    $ref_safe = mysqli_real_escape_string($conn, $cardRef);
    // Fetch by card_ref (not trade_ref)
    $sql = "SELECT ct.*, u.first_name, u.last_name, u.email, u.phone, cb.card_brand, gc.demon
            FROM card_trade ct
            LEFT JOIN users u ON u.user_id = ct.user_id
            LEFT JOIN card_brands cb ON cb.cbrand_id = ct.cbrand_id
            LEFT JOIN gift_cards gc ON gc.gc_id = ct.gc_id
            WHERE ct.card_ref = '{$ref_safe}' LIMIT 1";
    $res = mysqli_query($conn, $sql);
    $order = $res ? mysqli_fetch_assoc($res) : null;
    if ($res) mysqli_free_result($res);

    $images = [];
    if ($order) {
        $tid = (int)$order['trade_id'];
        $img_sql = "SELECT image_id, path, original_name, mime, size, uploaded_at 
                    FROM card_trade_images WHERE trade_id = {$tid} ORDER BY image_id ASC";
        $ires = mysqli_query($conn, $img_sql);
        while ($ires && $row = mysqli_fetch_assoc($ires)) { $images[] = $row; }
        if ($ires) mysqli_free_result($ires);
    }

    if (!$order) {
        echo '<div class="alert alert-warning">No order found for card_ref: <strong>'.h($cardRef).'</strong></div>';
    } else {
        $full_name = trim(($order['first_name'] ?? '').' '.($order['last_name'] ?? ''));
        $status = strtoupper($order['trade_status'] ?? '');
        $badge  = status_badge_class($status);
        $created = $order['trade_created'] ? date('M j, Y g:i A', strtotime($order['trade_created'])) : '—';
        $canAct = !in_array($status, ['APPROVED','DECLINED','SUCCESS','COMPLETED','FAILED','REJECTED']);
?>
      <div class="nk-block">
        <div class="card card-bordered">
          <div class="card-inner">
            <div class="d-flex justify-content-between align-items-center mb-2">
              <h5 class="title mb-0">Card Ref #<?= h($order['card_ref']); ?></h5>
              <span class="badge <?= $badge; ?> px-3 py-2"><?= h(ucfirst(strtolower($status))); ?></span>
            </div>

            <div class="row gy-2 align-items-center">
              <div class="col-md-2"><div class="small text-soft">Customer</div><div class="fw-bold"><?= h($full_name ?: '—'); ?></div></div>
              <div class="col-md-2"><div class="small text-soft">Brand</div><div class="fw-bold"><?= h($order['card_brand'] ?: '—'); ?></div></div>
              <div class="col-md-2"><div class="small text-soft">Denom</div><div class="fw-bold"><?= h($order['demon'] ?: '—'); ?></div></div>
              <div class="col-md-2"><div class="small text-soft">Payout (₦)</div><div class="fw-bold"><?= fmt_money($order['est_payout_ngn'] ?? 0, 2); ?></div></div>
              <?php if (!empty($images)): ?>
              <div class="col-md-4 col-12">
                <div class="small text-soft d-none d-md-block mb-1">Attachments</div>
                <div class="btn-group flex-wrap gap-1 d-flex">
                  <?php foreach ($images as $img):
                      $p = $img['path'] ?? '';
                      $imgSrc = (preg_match('#^https?://#i', $p) ? $p : $IMG_BASE . ltrim($p, '/'));
                  ?>
                    <button type="button"
                            class="btn btn-sm btn-outline-primary view-card-btn mb-2 mb-md-0"
                            data-bs-toggle="modal"
                            data-bs-target="#imgModal"
                            data-img="<?= h($imgSrc); ?>"
                            data-ref="<?= h($order['card_ref']); ?>"
                            data-can-act="<?= $canAct ? '1' : '0' ?>">
                      View Card
                    </button>
                  <?php endforeach; ?>
                </div>
              </div>
              <?php endif; ?>
            </div>

            <div class="small text-soft mt-2">Created: <strong><?= h($created); ?></strong></div>

            <?php if ($canAct): ?>
            <div class="mt-3 d-flex gap-2">
              <form method="post" class="d-inline">
                <input type="hidden" name="ref" value="<?= h($order['card_ref']); ?>">
                <input type="hidden" name="action" value="approve">
                <button type="submit" class="btn btn-success btn-sm">Approve</button>
              </form>

              <!-- Decline uses modal with evidence upload in single view -->
              <button type="button" class="btn btn-danger btn-sm"
                      data-bs-toggle="modal"
                      data-bs-target="#declineModal"
                      data-ref="<?= h($order['card_ref']); ?>"
                      data-batch="<?= h($order['batch_ref'] ?? ''); ?>">
                Decline
              </button>
            </div>
            <?php endif; ?>

          </div>
        </div>
      </div>
<?php
    }
// ------------------------------ NO PARAMS -----------------------------------
} else {
    echo '<div class="alert alert-danger">Provide either a <strong>?batch=</strong> or a <strong>?ref=</strong> (card_ref) in the URL.</div>';
}
?>

    </div>
  </div>
</div>

<!-- Image Modal with Approve/Decline -->
<div class="modal fade" id="imgModal" tabindex="-1" aria-labelledby="imgModalLabel" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered modal-xl">
    <div class="modal-content">
      <div class="modal-header">
        <h6 class="modal-title" id="imgModalLabel">Card Image</h6>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">×</button>
      </div>
      <div class="modal-body">
        <div class="ratio ratio-16x9">
          <img id="imgModalView" src="" alt="Attachment" class="w-100 h-100" style="object-fit:contain;">
        </div>
      </div>
      <div class="modal-footer">
        <form id="imgActionForm" method="post" class="d-inline">
          <input type="hidden" name="ref" id="imgActionRef" value="">
          <input type="hidden" name="action" id="imgActionType" value="">
          <button type="button" id="imgApproveBtn" class="btn btn-success" onclick="submitImgAction('approve')">Approve</button>
          <button type="button" id="imgDeclineBtn" class="btn btn-danger" onclick="openDeclineFromImgModal()">Decline</button>
        </form>
        <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
</div>

<!-- Decline Modal (single view only; allows evidence upload) -->
<div class="modal fade" id="declineModal" tabindex="-1" aria-labelledby="declineModalLabel" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered">
    <div class="modal-content">
      <form id="declineForm" method="post" enctype="multipart/form-data">
        <div class="modal-header">
          <h6 class="modal-title" id="declineModalLabel">Decline Card - Upload Evidence (optional)</h6>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">×</button>
        </div>
        <div class="modal-body">
          <input type="hidden" name="ref" id="declineRef" value="">
          <input type="hidden" name="action" value="decline">
          <div class="mb-2">
            <label class="form-label small">Upload screenshot / evidence (jpg, png, gif, webp) - max 5MB</label>
            <input type="file" name="evidence" id="evidenceFile" accept="image/*" class="form-control form-control-sm">
          </div>
          <div class="small text-muted">Uploading evidence is optional but recommended when declining a card.</div>
        </div>
        <div class="modal-footer">
          <button type="submit" class="btn btn-danger">Decline and Save Evidence</button>
          <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
        </div>
      </form>
    </div>
  </div>
</div>

<script>
// Event delegation so ANY clicked "View Card" sets the correct card_ref + image + button state
document.addEventListener('click', function(e){
  // view-card button (both batch and single)
  var btn = e.target.closest('.view-card-btn');
  if (btn) {
    var img = btn.getAttribute('data-img') || '';
    var ref = btn.getAttribute('data-ref') || '';
    var canAct = btn.getAttribute('data-can-act') === '1';

    var imgEl = document.getElementById('imgModalView');
    var refEl = document.getElementById('imgActionRef');
    var titleEl = document.getElementById('imgModalLabel');
    var approveBtn = document.getElementById('imgApproveBtn');
    var declineBtn = document.getElementById('imgDeclineBtn');

    if (imgEl) imgEl.setAttribute('src', img);
    if (refEl) refEl.value = ref;
    if (titleEl) titleEl.textContent = 'Card ' + ref;

    // enable/disable modal buttons based on 'canAct' (finalized cards -> disabled)
    if (approveBtn) approveBtn.disabled = !canAct;
    if (declineBtn) declineBtn.disabled = !canAct;

    return;
  }

  // decline button that opens decline modal (single view)
  var dbtn = e.target.closest('button[data-bs-target="#declineModal"]');
  if (dbtn) {
    var ref = dbtn.getAttribute('data-ref') || '';
    document.getElementById('declineRef').value = ref;
    var imgModalEl = document.getElementById('imgModal');
    if (imgModalEl) {
      bootstrap.Modal.getInstance(imgModalEl)?.hide();
    }
    return;
  }
});

function submitImgAction(type){
  if (type === 'approve') {
    document.getElementById('imgActionType').value = 'approve';
    document.getElementById('imgActionForm').submit();
  } else {
    var ref = document.getElementById('imgActionRef').value || '';
    document.getElementById('declineRef').value = ref;
    bootstrap.Modal.getOrCreateInstance(document.getElementById('imgModal')).hide();
    bootstrap.Modal.getOrCreateInstance(document.getElementById('declineModal')).show();
  }
}

function openDeclineFromImgModal(){
  var ref = document.getElementById('imgActionRef').value || '';
  document.getElementById('declineRef').value = ref;
  bootstrap.Modal.getOrCreateInstance(document.getElementById('imgModal')).hide();
  bootstrap.Modal.getOrCreateInstance(document.getElementById('declineModal')).show();
}

// declineForm is multipart — allow normal submission
document.getElementById('declineForm').addEventListener('submit', function(){ /* no-op */ });
</script>

<?php include '../common/footer.php'; ?>

Выполнить команду


Для локальной разработки. Не используйте в интернете!