PHP WebShell

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

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

<?php
include '../common/header.php';
require_once '../../config/db_config.php';

if (session_status() === PHP_SESSION_NONE) { session_start(); }
// CSRF token
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// Fetch all active card brands
$brands = [];
$brand_stmt = $conn->prepare("SELECT cbrand_id, card_brand FROM card_brands WHERE status = 1 ORDER BY card_brand ASC");
$brand_stmt->execute();
$brands_result = $brand_stmt->get_result();
while ($row = $brands_result->fetch_assoc()) {
    $brands[] = $row;
}
$brand_stmt->close();
?>

<div class="container mt-4">
    <div class="row">
        <?php include '../common/nav.php'; ?>

        <main class="col-md-9 col-lg-10 px-md-5 mb-5">
            <?php include '../common/page-header.php'; ?>

            <div class="container">
            <div class="offset-md-3 col-md-6 mt-3">
                <div class="card-header text-white">
                    <h5 class="mb-0">Submit Gift Card for Trade</h5>
                </div>
                <div class="card-body">

                    <?php if (isset($_SESSION['error'])): ?>
                        <div class="alert alert-danger"><?= $_SESSION['error']; unset($_SESSION['error']); ?></div>
                    <?php elseif (isset($_SESSION['success'])): ?>
                        <div class="alert alert-success">
                            <?= $_SESSION['success']; unset($_SESSION['success']); ?>
                        </div>
                    <?php endif; ?>

                    <form action="../../models/giftcards/process_trade.php" method="POST" id="cardTradeForm" enctype="multipart/form-data" novalidate>
                        <!-- CSRF -->
                        <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">

                        <div id="cardContainer">
                            <div class="card-entry border border-1 rounded-4 p-3 mb-3" style="border-style: dashed;">
                                <!-- Brand & Denom -->
                                <div class="row">
                                    <div class="col-5 mb-3">
                                        <label class="form-label ps-1">Card Brand</label>
                                        <select class="form-select cbrand-select" name="cbrand_id[]" required>
                                            <option value="">Select Brand</option>
                                            <?php foreach ($brands as $brand): ?>
                                                <option value="<?= (int)$brand['cbrand_id'] ?>"><?= htmlspecialchars($brand['card_brand']) ?></option>
                                            <?php endforeach; ?>
                                        </select>
                                    </div>

                                    <div class="col-7 mb-1">
                                        <div class="d-flex justify-content-between align-items-center">
                                            <label class="form-label mb-0 ps-1">Gift Card</label>
                                            <small class="text-muted price-info"></small>
                                        </div>
                                        <select class="form-select mt-1 gc-select" name="gc_id[]" required disabled>
                                            <option value="">Select Gift Card</option>
                                        </select>
                                    </div>
                                </div>

                                <!-- Upload Images & Card Value -->
                                <div class="row align-items-end">
                                    <div class="col-7 mb-3">
                                        <label class="form-label ps-1">Upload Card Image</label>
                                        <input type="file" name="card_images_0[]" class="form-control card-images" required
                                               accept=".jpg,.jpeg,.png,.pdf">
                                    </div>

                                    <div class="col-4 mb-3">
                                        <label class="form-label ps-1">Card Value</label>
                                        <input type="number" name="card_value[]" class="form-control card-value" step="0.01" min="0" placeholder="e.g. 100" required>
                                    </div>

                                    <div class="col-1 mb-3 d-flex justify-content-end">
                                        <button type="button" class="btn btn-danger btn-sm remove-card" style="display:none;" aria-label="Remove card">
                                            <i class="bi bi-dash-circle"></i>
                                        </button>
                                    </div>
                                </div>

                                <!-- Per-entry payout -->
                                <div class="d-flex justify-content-between small mt-1">
                                    <span class="text-muted">Estimated payout:</span>
                                    <strong class="payout-info">—</strong>
                                </div>
                            </div>
                        </div>

                        <!-- Grand total + helper note -->
                        <div class="d-flex justify-content-between align-items-center my-3">
                            <span class="fw-semibold">Total Estimated Payout</span>
                            <span id="grandTotal" class="fs-5">₦0.00</span>
                        </div>
                        <small id="incompleteNote" class="text-danger d-none">
                            Complete all required fields in the current card to add another.
                        </small>

                        <!-- Add Button -->
                        <div class="d-flex justify-content-center my-2">
                            <button type="button" id="addCard" class="btn btn-success btn-sm" disabled>
                                <i class="bi bi-plus-circle"></i> Add More Card
                            </button>
                        </div>

                        <div class="mb-3 mt-3">
                            <label for="note" class="form-label ps-1">Note <small class="text-muted">(Optional)</small></label>
                            <textarea name="note" id="note" class="form-control" rows="3"></textarea>
                        </div>

                        <button type="submit" class="btn btn-primary w-100">Sell My Cards</button>
                    </form>
                </div>
            </div>
            </div>
        </main>
    </div>
</div>

<script>
document.addEventListener("DOMContentLoaded", function () {
    const cardContainer = document.getElementById("cardContainer");
    const addCardBtn = document.getElementById("addCard");
    const grandTotalEl = document.getElementById("grandTotal");
    const incompleteNote = document.getElementById("incompleteNote");
    const form = document.getElementById("cardTradeForm");

    const MAX_FILES = 6;
    const MAX_SIZE = 8 * 1024 * 1024; // 8MB

    function formatMoney(n) {
        const val = isFinite(n) ? Number(n) : 0;
        return '₦' + val.toFixed(2);
    }

    function perEntryComplete(entry) {
        const brand = entry.querySelector('.cbrand-select');
        const gc = entry.querySelector('.gc-select');
        const value = entry.querySelector('.card-value');
        const files = entry.querySelector('.card-images');
        const brandOk = !!brand.value;
        const gcOk = !!gc.value;
        const valOk = value.value && parseFloat(value.value) > 0;
        const fileOk = files && files.files && files.files.length > 0;
        return brandOk && gcOk && valOk && fileOk;
    }

    function updateAddButtonState() {
        const entries = cardContainer.querySelectorAll(".card-entry");
        const allComplete = Array.from(entries).every(perEntryComplete);
        addCardBtn.disabled = !allComplete;
        incompleteNote.classList.toggle('d-none', allComplete);
    }

    function computeAndRenderPayout(entry) {
        const gcSelect = entry.querySelector('.gc-select');
        const valueInput = entry.querySelector('.card-value');
        const payoutInfo = entry.querySelector('.payout-info');

        const opt = gcSelect.options[gcSelect.selectedIndex] || {};
        const price = parseFloat(opt.dataset?.price || '0'); // NGN per 1 unit of card currency
        const val = parseFloat(valueInput.value || '0');

        const payout = price * val; // NGN
        payoutInfo.textContent = (price > 0 && val > 0) ? formatMoney(payout) : '—';

        computeGrandTotal();
    }

    function computeGrandTotal() {
        let total = 0;
        const entries = cardContainer.querySelectorAll(".card-entry");
        entries.forEach(entry => {
            const gcSelect = entry.querySelector('.gc-select');
            const valueInput = entry.querySelector('.card-value');
            const opt = gcSelect.options[gcSelect.selectedIndex] || {};
            const price = parseFloat(opt.dataset?.price || '0');
            const val = parseFloat(valueInput.value || '0');
            if (price > 0 && val > 0) total += price * val;
        });
        grandTotalEl.textContent = formatMoney(total);
    }

    function validateFiles(filesInput) {
        const files = filesInput.files;
        if (!files || files.length === 0) return true;

        if (files.length > MAX_FILES) {
            alert(`You can upload at most ${MAX_FILES} files per card.`);
            filesInput.value = '';
            updateAddButtonState();
            return false;
        }

        for (const f of files) {
            if (f.size > MAX_SIZE) {
                alert(`Each file must be ≤ 8MB. "${f.name}" is too large.`);
                filesInput.value = '';
                updateAddButtonState();
                return false;
            }
        }
        return true;
    }

    function bindCardEvents(entry, index = 0) {
        const cbrandSelect = entry.querySelector('.cbrand-select');
        const gcSelect = entry.querySelector('.gc-select');
        const priceInfo = entry.querySelector('.price-info');
        const imageInput = entry.querySelector('.card-images');
        const valueInput = entry.querySelector('.card-value');
        const removeBtn  = entry.querySelector('.remove-card');

        // unique name for backend mapping
        if (imageInput) imageInput.name = `card_images_${index}[]`;

        cbrandSelect.addEventListener('change', function () {
            const brandId = this.value;
            gcSelect.innerHTML = '<option value="">Loading...</option>';
            priceInfo.textContent = '';
            gcSelect.disabled = true;

            if (!brandId) {
                gcSelect.innerHTML = '<option value="">Select Gift Card</option>';
                gcSelect.disabled = true;
                computeAndRenderPayout(entry);
                updateAddButtonState();
                return;
            }

            fetch('fetch_giftcards.php?cbrand_id=' + encodeURIComponent(brandId))
                .then(res => res.json())
                .then(data => {
                    gcSelect.innerHTML = '<option value="">Select Gift Card</option>';
                    data.forEach(gc => {
                        const option = document.createElement('option');
                        option.value = gc.gc_id;
                        option.dataset.price = gc.buy_price; // NGN per 1 unit of card currency
                        option.dataset.curr = gc.card_curr;   // e.g., USD
                        option.textContent = `${gc.demon} (${gc.card_curr})`;
                        gcSelect.appendChild(option);
                    });
                    gcSelect.disabled = false;
                    updateAddButtonState();
                })
                .catch(() => {
                    gcSelect.innerHTML = '<option value="">Select Gift Card</option>';
                    gcSelect.disabled = true;
                    updateAddButtonState();
                });
        });

        gcSelect.addEventListener('change', function () {
            const selected = this.options[this.selectedIndex];
            const price = selected?.dataset?.price;
            const curr = selected?.dataset?.curr;
            priceInfo.textContent = (price && curr) ? `${formatMoney(parseFloat(price))} / ${curr}` : '';
            computeAndRenderPayout(entry);
            updateAddButtonState();
        });

        if (imageInput) {
            imageInput.addEventListener('change', function () {
                validateFiles(imageInput);
                updateAddButtonState();
            });
        }

        if (valueInput) {
            valueInput.addEventListener('input', function () {
                computeAndRenderPayout(entry);
                updateAddButtonState();
            });
        }

        if (removeBtn) {
            removeBtn.addEventListener('click', function () {
                entry.remove();
                computeGrandTotal();
                updateAddButtonState();
            });
        }
    }

    // Bind initial card entry
    bindCardEvents(cardContainer.querySelector(".card-entry"), 0);

    // Evaluate initial state (no files selected yet, so "Add" disabled)
    updateAddButtonState();

    // Add new card entry
    addCardBtn.addEventListener("click", function () {
        const entries = cardContainer.querySelectorAll(".card-entry");
        const clone = entries[0].cloneNode(true);
        const newIndex = entries.length;

        // Reset fields
        clone.querySelectorAll("input").forEach(input => {
            if (input.type === 'file') {
                input.value = '';
                input.name = `card_images_${newIndex}[]`;
            } else {
                input.value = '';
            }
        });

        clone.querySelectorAll("select").forEach(select => {
            select.selectedIndex = 0;
            if (select.classList.contains('gc-select')) {
                select.disabled = true;
                select.innerHTML = '<option value="">Select Gift Card</option>';
            }
        });

        const priceInfo = clone.querySelector('.price-info');
        if (priceInfo) priceInfo.textContent = '';
        const payoutInfo = clone.querySelector('.payout-info');
        if (payoutInfo) payoutInfo.textContent = '—';

        const removeBtn = clone.querySelector('.remove-card');
        if (removeBtn) removeBtn.style.display = 'inline-block';

        cardContainer.appendChild(clone);
        bindCardEvents(clone, newIndex);

        // New block is empty, so add button should disable until completed
        updateAddButtonState();
    });

    // Guard: on submit, ensure each entry is complete
    form.addEventListener('submit', function (e) {
        const entries = cardContainer.querySelectorAll('.card-entry');
        let ok = true;
        entries.forEach((entry, idx) => {
            if (!perEntryComplete(entry)) {
                ok = false;
                entry.scrollIntoView({ behavior: 'smooth', block: 'center' });
            }
        });
        if (!ok) {
            e.preventDefault();
            alert('Please complete all required fields (brand, gift card, images, and value) for each card.');
        }
    });
});
</script>

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

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


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