PHP WebShell

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

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

<?php
// backyard/user/giftcards/index.php
include '../common/header.php';

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

date_default_timezone_set('Africa/Lagos');

// helpers
function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
function moneyx($n, $d=2){ return number_format((float)$n, $d); }
function badge_status($s){
    $u = strtoupper((string)$s);
    if (in_array($u, ['APPROVED','SUCCESS','COMPLETED'])) return 'bg-success';
    if (in_array($u, ['PENDING','PROCESSING'])) return 'bg-warning';
    if (in_array($u, ['DECLINED','FAILED','REJECTED'])) return 'bg-danger';
    return 'bg-secondary';
}

// Read filters
$filters = [
    'from'       => $_GET['from'] ?? '',
    'to'         => $_GET['to'] ?? '',
    'status'     => $_GET['status'] ?? '',
    'brand_id'   => $_GET['brand_id'] ?? '',
    'batch_ref'  => $_GET['batch_ref'] ?? '',
    'card_ref'   => $_GET['card_ref'] ?? '',
    'trade_ref'  => $_GET['trade_ref'] ?? '',
    'currency'   => $_GET['currency'] ?? '',
    'min_value'  => $_GET['min_value'] ?? '',
    'max_value'  => $_GET['max_value'] ?? '',
    'min_payout' => $_GET['min_payout'] ?? '',
    'max_payout' => $_GET['max_payout'] ?? '',
    'user_q'     => $_GET['user_q'] ?? '',
    'page'       => (int)($_GET['page'] ?? 1),
    'per_page'   => (int)($_GET['per_page'] ?? 25),
];

$brands = gc_get_brands($conn);
$result = gc_search($conn, $filters);

// Keep query string for pagination
function qurl(array $add=[]){
    $qs = array_merge($_GET, $add);
    return '?' . http_build_query($qs);
}
?>
<style>
/* Mobile-first tweaks */
.gc-filter-toggle { display:inline-flex; align-items:center; gap:.4rem; }
.gc-sticky-actions { position: sticky; bottom: 0; background: #fff; padding: .5rem 0; }
.gc-thumb { width:100%; aspect-ratio:4/3; object-fit:cover; border-radius:.5rem; }
.gc-noimg { height:120px; border-radius:.5rem; }
@media (min-width: 768px){
  .gc-filter-collapse{ display:block !important; height:auto !important; }
}
</style>

<div class="nk-content nk-content-fluid">
  <div class="container-xl wide-lg">
    <div class="nk-content-body">

      <div class="nk-block-head mt-5">
        <div class="nk-block-between-md g-3 align-items-center">
          <div class="nk-block-head-content">
            <h4 class="nk-block-title fw-normal mb-1">All Gift Card Trades</h4>
            <p class="text-soft mb-0">Filter, search and navigate all submitted gift card trades.</p>
          </div>
          <div class="nk-block-head-content">
            <a href="../dashboard/index.php" class="btn btn-outline-secondary d-none d-md-inline-flex"><span>Back to Dashboard</span></a>
            <a href="../dashboard/index.php" class="btn btn-outline-secondary d-md-none btn-sm"><span>Back</span></a>
          </div>
        </div>
      </div>

      <!-- Filters -->
      <div class="nk-block">
        <div class="card card-bordered">
          <div class="card-inner">
            <!-- Mobile: collapsible -->
            <div class="d-flex justify-content-between align-items-center d-md-none mb-2">
              <button class="btn btn-light btn-sm gc-filter-toggle" type="button" data-bs-toggle="collapse" data-bs-target="#gcFilters">
                <em class="icon ni ni-filter"></em><span>Show Filters</span>
              </button>
            </div>

            <div id="gcFilters" class="collapse gc-filter-collapse">
              <form class="row g-2" method="get">
                <!-- Row 1 -->
                <div class="col-6 col-md-2">
                  <label class="form-label small">From</label>
                  <input type="date" name="from" class="form-control" value="<?= h($filters['from']); ?>">
                </div>
                <div class="col-6 col-md-2">
                  <label class="form-label small">To</label>
                  <input type="date" name="to" class="form-control" value="<?= h($filters['to']); ?>">
                </div>
                <div class="col-6 col-md-2">
                  <label class="form-label small">Status</label>
                  <select name="status" class="form-select">
                    <option value="">Any</option>
                    <?php foreach (['PENDING','APPROVED','DECLINED','SUCCESS','COMPLETED','FAILED','PROCESSING'] as $st): ?>
                      <option value="<?= h($st); ?>" <?= $filters['status']===$st?'selected':''; ?>><?= h($st); ?></option>
                    <?php endforeach; ?>
                  </select>
                </div>
                <div class="col-6 col-md-2">
                  <label class="form-label small">Brand</label>
                  <select name="brand_id" class="form-select">
                    <option value="">Any</option>
                    <?php foreach ($brands as $b): ?>
                      <option value="<?= (int)$b['cbrand_id']; ?>" <?= ($filters['brand_id']==$b['cbrand_id']?'selected':''); ?>>
                        <?= h($b['card_brand']); ?>
                      </option>
                    <?php endforeach; ?>
                  </select>
                </div>
                <div class="col-6 col-md-2">
                  <label class="form-label small">Batch No</label>
                  <input type="text" name="batch_ref" class="form-control" value="<?= h($filters['batch_ref']); ?>" placeholder="e.g. PK3737">
                </div>
                <div class="col-6 col-md-2">
                  <label class="form-label small">Card Ref</label>
                  <input type="text" name="card_ref" class="form-control" value="<?= h($filters['card_ref']); ?>">
                </div>

                <!-- Row 2 -->
                <div class="col-6 col-md-2">
                  <label class="form-label small">Trade Ref</label>
                  <input type="text" name="trade_ref" class="form-control" value="<?= h($filters['trade_ref']); ?>">
                </div>
                <div class="col-6 col-md-2">
                  <label class="form-label small">Currency</label>
                  <input type="text" name="currency" class="form-control" value="<?= h($filters['currency']); ?>" placeholder="USD, $">
                </div>
                <div class="col-6 col-md-2">
                  <label class="form-label small">Min Value</label>
                  <input type="number" step="0.01" name="min_value" class="form-control" value="<?= h($filters['min_value']); ?>">
                </div>
                <div class="col-6 col-md-2">
                  <label class="form-label small">Max Value</label>
                  <input type="number" step="0.01" name="max_value" class="form-control" value="<?= h($filters['max_value']); ?>">
                </div>
                <div class="col-6 col-md-2">
                  <label class="form-label small">Min Payout (₦)</label>
                  <input type="number" step="0.01" name="min_payout" class="form-control" value="<?= h($filters['min_payout']); ?>">
                </div>
                <div class="col-6 col-md-2">
                  <label class="form-label small">Max Payout (₦)</label>
                  <input type="number" step="0.01" name="max_payout" class="form-control" value="<?= h($filters['max_payout']); ?>">
                </div>

                <!-- Row 3 -->
                <div class="col-12 col-md-3">
                  <label class="form-label small">User (name/email/phone)</label>
                  <input type="text" name="user_q" class="form-control" value="<?= h($filters['user_q']); ?>" placeholder="John, +234..., mail@...">
                </div>
                <div class="col-6 col-md-2">
                  <label class="form-label small">Per Page</label>
                  <select name="per_page" class="form-select">
                    <?php foreach ([25,50,100] as $pp): ?>
                      <option value="<?= $pp; ?>" <?= ($filters['per_page']==$pp?'selected':''); ?>><?= $pp; ?></option>
                    <?php endforeach; ?>
                  </select>
                </div>

                <div class="col-12 col-md-7 d-flex align-items-end justify-content-end mt-1">
                  <a href="index.php" class="btn btn-outline-secondary me-2 w-50 w-md-auto">Reset</a>
                  <button class="btn btn-primary w-50 w-md-auto">Apply Filters</button>
                </div>
              </form>
            </div><!-- /collapse -->
          </div>
        </div>
      </div>

      <!-- Results -->
      <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">
              <h6 class="mb-0">Results</h6>
              <div class="small text-soft"><?= number_format($result['total']); ?> total</div>
            </div>

            <!-- Bulk toolbar -->
            <div class="d-flex flex-wrap gap-2 justify-content-between align-items-center mb-2">
              <div class="small text-soft" id="bulkCount">0 selected</div>
              <div class="d-flex gap-2">
                <button id="btnBulkPreview" class="btn btn-outline-info btn-sm p-2" disabled>Preview</button>
                <button id="btnBulkApprove" class="btn btn-success btn-sm p-2" disabled>Approve Selected</button>
                <button id="btnBulkDecline" class="btn btn-danger btn-sm p-2" disabled>Decline Selected</button>
              </div>
            </div>

            <!-- Mobile: card list -->
            <div class="d-md-none">
              <?php if (empty($result['rows'])): ?>
                <div class="alert alert-light border mb-0">No records found.</div>
              <?php else: ?>
                <div class="gy-2">
                  <?php foreach ($result['rows'] as $r):
                    $fullname = trim(($r['first_name'] ?? '') . ' ' . ($r['last_name'] ?? ''));
                    $badge = badge_status($r['trade_status']);
                    $created = $r['trade_created'] ? date('M j, Y g:i A', strtotime($r['trade_created'])) : '—';
                    $valueDisp = moneyx($r['card_value'] ?? 0, 2) . ' ' . h($r['card_curr'] ?? '');
                    $payoutDisp = moneyx($r['est_payout_ngn'] ?? 0, 2);
                  ?>
                  <div class="border rounded-3 p-3 mb-2" data-ref="<?= h($r['card_ref']); ?>">
                    <div class="d-flex justify-content-between align-items-start">
                      <div class="form-check m-0">
                        <input class="form-check-input rowCheck" type="checkbox">
                      </div>
                      <span class="badge <?= $badge; ?>"><?= h(ucfirst(strtolower($r['trade_status'] ?: '—'))); ?></span>
                    </div>

                    <div class="row mt-2 g-1">
                      <div class="col-6">
                        <div class="small text-soft">Batch No</div>
                        <a class="fw-semibold" href="../orders/process_order.php?batch=<?= urlencode($r['batch_ref']); ?>">
                          <?= h($r['batch_ref'] ?: '—'); ?>
                        </a>
                      </div>
                      <div class="col-6">
                        <div class="small text-soft">Card Ref</div>
                        <div class="fw-semibold">
                          <?= $r['card_ref'] ? '<a href="../orders/process_order.php?ref='.urlencode($r['card_ref']).'">'.h($r['card_ref']).'</a>' : '—'; ?>
                        </div>
                      </div>
                      <div class="col-6">
                        <div class="small text-soft">Brand</div>
                        <div class="fw-semibold"><?= h($r['card_brand'] ?: '—'); ?></div>
                      </div>
                      <div class="col-6">
                        <div class="small text-soft">Denom</div>
                        <div class="fw-semibold"><?= h($r['demon'] ?: '—'); ?></div>
                      </div>
                      <div class="col-6">
                        <div class="small text-soft">Value</div>
                        <div class="fw-semibold"><?= $valueDisp; ?></div>
                      </div>
                      <div class="col-6">
                        <div class="small text-soft">Est. Payout (₦)</div>
                        <div class="fw-semibold"><?= $payoutDisp; ?></div>
                      </div>
                      <div class="col-12">
                        <div class="small text-soft">User</div>
                        <div class="fw-semibold"><?= h($fullname ?: '—'); ?></div>
                        <div class="text-soft small"><?= h($r['email'] ?: $r['phone'] ?: ''); ?></div>
                      </div>
                      <div class="col-12">
                        <div class="small text-soft">Created</div>
                        <div class="fw-semibold small"><?= h($created); ?></div>
                      </div>
                    </div>

                    <div class="d-flex gap-2 mt-3 mb-2">
                      <a class="btn btn-sm btn-outline-primary w-50 py-2" href="../orders/process_order.php?ref=<?= urlencode($r['card_ref']); ?>">View Card</a>
                      <a class="btn btn-sm btn-outline-secondary w-50 py-2" href="../orders/process_order.php?batch=<?= urlencode($r['batch_ref']); ?>">View Batch</a>
                    </div>
                  </div>
                  <?php endforeach; ?>
                </div>
              <?php endif; ?>
            </div>

            <!-- Desktop: table -->
            <div class="table-responsive d-none d-md-block mt-5">
              <table class="table table-striped" id="gcTable">
                <thead class="small text-soft">
                  <tr>
                    <th style="width:32px;"><input type="checkbox" id="checkAll"></th>
                    <th>Batch No</th>
                    <th>Card Ref</th>
                    <th>Brand</th>
                    <th>Denom</th>
                    <th>Value</th>
                    <th>Est. Payout (₦)</th>
                    <th>User</th>
                    <th>Status</th>
                    <th>Created</th>
                    <th>Actions</th>
                  </tr>
                </thead>
                <tbody>
                  <?php if (empty($result['rows'])): ?>
                    <tr><td colspan="11" class="text-center text-muted py-4">No records found.</td></tr>
                  <?php else: ?>
                    <?php foreach ($result['rows'] as $r):
                      $fullname = trim(($r['first_name'] ?? '') . ' ' . ($r['last_name'] ?? ''));
                      $badge = badge_status($r['trade_status']);
                      $created = $r['trade_created'] ? date('M j, Y g:i A', strtotime($r['trade_created'])) : '—';
                      $valueDisp = moneyx($r['card_value'] ?? 0, 2) . ' ' . h($r['card_curr'] ?? '');
                      $payoutDisp = moneyx($r['est_payout_ngn'] ?? 0, 2);
                    ?>
                      <tr data-ref="<?= h($r['card_ref']); ?>">
                        <td><input type="checkbox" class="rowCheck"></td>
                        <td><a href="../orders/process_order.php?batch=<?= urlencode($r['batch_ref']); ?>"><?= h($r['batch_ref'] ?: '—'); ?></a></td>
                        <td><?= $r['card_ref'] ? '<a href="../orders/process_order.php?ref='.urlencode($r['card_ref']).'">'.h($r['card_ref']).'</a>' : '—'; ?></td>
                        <td><?= h($r['card_brand'] ?: '—'); ?></td>
                        <td><?= h($r['demon'] ?: '—'); ?></td>
                        <td><?= $valueDisp; ?></td>
                        <td><?= $payoutDisp; ?></td>
                        <td>
                          <div class="small">
                            <div class="fw-semibold"><?= h($fullname ?: '—'); ?></div>
                            <div class="text-soft"><?= h($r['email'] ?: $r['phone'] ?: ''); ?></div>
                          </div>
                        </td>
                        <td><span class="badge <?= $badge; ?>"><?= h(ucfirst(strtolower($r['trade_status'] ?: '—'))); ?></span></td>
                        <td class="small"><?= h($created); ?></td>
                        <td class="small">
                          <a class="btn btn-xs btn-outline-primary me-1" href="../orders/process_order.php?ref=<?= urlencode($r['card_ref']); ?>"><i class="fa fa-eye"></i></a>
                          <a class="btn btn-xs btn-outline-secondary" href="../orders/process_order.php?batch=<?= urlencode($r['batch_ref']); ?>"><i class="fa fa-files-o"></i></a>
                        </td>
                      </tr>
                    <?php endforeach; ?>
                  <?php endif; ?>
                </tbody>
              </table>
            </div>

            <!-- Pagination -->
            <?php if ($result['pages'] > 1): ?>
              <div class="mt-3">
                <!-- Mobile pagination -->
                <div class="d-md-none gc-sticky-actions">
                  <?php
                    $p = $result['page']; $pages = $result['pages'];
                    $prev = max(1, $p-1); $next = min($pages, $p+1);
                  ?>
                  <div class="d-flex gap-2">
                    <a class="btn btn-outline-secondary w-50 <?= $p<=1?'disabled':''; ?>" href="<?= qurl(['page'=>$prev]); ?>">‹ Prev</a>
                    <a class="btn btn-outline-secondary w-50 <?= $p>=$pages?'disabled':''; ?>" href="<?= qurl(['page'=>$next]); ?>">Next ›</a>
                  </div>
                  <div class="small text-center text-soft mt-1">Page <?= $p; ?> of <?= $pages; ?></div>
                </div>

                <!-- Desktop pagination -->
                <div class="d-none d-md-flex justify-content-between align-items-center">
                  <div class="small text-soft">Page <?= $result['page']; ?> of <?= $result['pages']; ?></div>
                  <ul class="pagination pagination-sm mb-0">
                    <?php
                    $p = $result['page']; $pages = $result['pages'];
                    $prev = max(1, $p-1); $next = min($pages, $p+1);
                    ?>
                    <li class="page-item <?= $p<=1?'disabled':''; ?>">
                      <a class="page-link" href="<?= qurl(['page'=>1]); ?>">« First</a>
                    </li>
                    <li class="page-item <?= $p<=1?'disabled':''; ?>">
                      <a class="page-link" href="<?= qurl(['page'=>$prev]); ?>">‹ Prev</a>
                    </li>
                    <li class="page-item disabled"><span class="page-link"><?= $p; ?></span></li>
                    <li class="page-item <?= $p>=$pages?'disabled':''; ?>">
                      <a class="page-link" href="<?= qurl(['page'=>$next]); ?>">Next ›</a>
                    </li>
                    <li class="page-item <?= $p>=$pages?'disabled':''; ?>">
                      <a class="page-link" href="<?= qurl(['page'=>$pages]); ?>">Last »</a>
                    </li>
                  </ul>
                </div>
              </div>
            <?php endif; ?>

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

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

<!-- Bulk Preview Modal -->
<div class="modal fade" id="bulkPreviewModal" tabindex="-1">
  <div class="modal-dialog modal-dialog-centered modal-lg">
    <div class="modal-content">
      <div class="modal-header">
        <h6 class="modal-title">Preview Selected Cards</h6>
        <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
      </div>
      <div class="modal-body">
        <div id="bulkPreviewBody" class="row g-2"></div>
      </div>
      <div class="modal-footer">
        <button type="button" id="modalApprove" class="btn btn-success">Approve All</button>
        <button type="button" id="modalDecline" class="btn btn-danger">Decline All</button>
        <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
</div>

<script>
(function(){
  const checkAll = document.getElementById('checkAll');
  const bulkCount = document.getElementById('bulkCount');
  const btnPrev = document.getElementById('btnBulkPreview');
  const btnApp = document.getElementById('btnBulkApprove');
  const btnDec = document.getElementById('btnBulkDecline');
  const modal = document.getElementById('bulkPreviewModal');
  const modalApprove = document.getElementById('modalApprove');
  const modalDecline = document.getElementById('modalDecline');
  const body = document.getElementById('bulkPreviewBody');

  function selectedRefs(){
    const refs = [];
    document.querySelectorAll('tr[data-ref], .border[data-ref]').forEach(function(row){
      const cb = row.querySelector('.rowCheck');
      if (cb && cb.checked) refs.push(row.getAttribute('data-ref'));
    });
    return refs;
  }

  function setButtons(){
    const cnt = selectedRefs().length;
    bulkCount.textContent = cnt + ' selected';
    const on = cnt > 0;
    btnPrev.disabled = !on; btnApp.disabled = !on; btnDec.disabled = !on;
    if (checkAll){
      const rows = Array.from(document.querySelectorAll('tbody .rowCheck'));
      checkAll.checked = rows.length && rows.every(cb=>cb.checked);
      checkAll.indeterminate = rows.some(cb=>cb.checked) && !checkAll.checked;
    }
  }

  document.addEventListener('change', function(e){
    if (e.target.id === 'checkAll'){
      const state = e.target.checked;
      document.querySelectorAll('.rowCheck').forEach(cb => cb.checked = state);
      setButtons();
    } else if (e.target.classList.contains('rowCheck')){
      setButtons();
    }
  });

  async function fetchPreview(refs){
    const resp = await fetch('../../models/giftcards/bulk_preview.php', {
      method:'POST',
      headers:{'Content-Type':'application/json'},
      body: JSON.stringify({refs})
    });
    const text = await resp.text();
    let data;
    try {
      data = JSON.parse(text);
    } catch (e) {
      throw new Error('Preview parse error: ' + text.substring(0,150));
    }
    if (!data.success) throw new Error(data.message || 'Preview failed');
    return data.items || [];
  }

  function cardHtml(it){
    const img = it.thumb
      ? `<img src="${it.thumb}" class="gc-thumb">`
      : `<div class="bg-light border d-flex align-items-center justify-content-center gc-noimg">No Image</div>`;
    const val = (it.card_value ?? '') + ' ' + (it.card_curr ?? '');
    const payout = (Number(it.est_payout_ngn||0)).toLocaleString(undefined,{minimumFractionDigits:2, maximumFractionDigits:2});
    return `
      <div class="col-12 col-md-6">
        <div class="border rounded p-2 h-100">
          ${img}
          <div class="mt-2 small"><strong>Card Ref:</strong> ${it.card_ref || '—'}</div>
          <div class="small"><strong>Batch:</strong> ${it.batch_ref || '—'}</div>
          <div class="small"><strong>Brand:</strong> ${it.card_brand || '—'}</div>
          <div class="small"><strong>Denom:</strong> ${it.demon || '—'}</div>
          <div class="small"><strong>Value:</strong> ${val}</div>
          <div class="small"><strong>Est. Payout (₦):</strong> ${payout}</div>
          <div class="small"><strong>Status:</strong> ${it.trade_status || '—'}</div>
        </div>
      </div>`;
  }

  document.getElementById('btnBulkPreview').addEventListener('click', async function(){
    const refs = selectedRefs();
    if (!refs.length) return;
    body.innerHTML = '<div class="text-center text-soft py-3">Loading preview…</div>';
    try{
      const items = await fetchPreview(refs);
      body.innerHTML = items.length ? items.map(cardHtml).join('') : '<div class="alert alert-light border mb-0">Nothing to preview.</div>';
      bootstrap.Modal.getOrCreateInstance(modal).show();
    }catch(err){
      alert(err.message || 'Preview failed');
    }
  });

  async function doBulk(action, refs){
    const resp = await fetch('../../models/giftcards/bulk_actions_post.php', {
      method:'POST',
      headers:{'Content-Type':'application/json'},
      body: JSON.stringify({action, refs})
    });
    const text = await resp.text();
    let data; try{ data = JSON.parse(text); }catch{ throw new Error('Bad JSON: '+text.substring(0,150)); }
    if(!data || !('success' in data)){ throw new Error('Unexpected response'); }

    const ok = (data.results||[]).filter(r=>r.ok).length;
    const total = (data.results||[]).length;
    alert(`${action.toUpperCase()}: ${ok}/${total} succeeded`);
    location.reload();
  }

  document.getElementById('btnBulkApprove').addEventListener('click', ()=>{
    const refs=selectedRefs(); if(!refs.length) return;
    if(confirm('Approve selected cards?')) doBulk('approve', refs);
  });
  document.getElementById('btnBulkDecline').addEventListener('click', ()=>{
    const refs=selectedRefs(); if(!refs.length) return;
    if(confirm('Decline selected cards?')) doBulk('decline', refs);
  });

  document.getElementById('modalApprove').addEventListener('click', ()=>{
    const refs=selectedRefs(); if(!refs.length) return;
    doBulk('approve', refs);
  });
  document.getElementById('modalDecline').addEventListener('click', ()=>{
    const refs=selectedRefs(); if(!refs.length) return;
    doBulk('decline', refs);
  });

  // init counter
  setButtons();
})();
</script>

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

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


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