Your IP : 216.73.217.78


Current Path : /home/seto/888mirror.pm/admin/
Upload File :
Current File : /home/seto/888mirror.pm/admin/partners.php

<?php
require_once __DIR__ . '/../includes/auth.php';
require_once __DIR__ . '/../includes/helpers.php';

auth_check();
$db = db();

$action = $_POST['action'] ?? '';

if ($action === 'save') {
    $pid   = (int)($_POST['id'] ?? 0);
    $name  = trim($_POST['name'] ?? '');
    $type  = in_array($_POST['type'] ?? '', ['direct','network']) ? $_POST['type'] : 'direct';
    $cname = trim($_POST['contact_name'] ?? '');
    $ctg   = trim($_POST['contact_telegram'] ?? '');
    $aurl  = trim($_POST['affiliate_url'] ?? '');
    $notes = trim($_POST['notes'] ?? '');
    if ($name) {
        if ($pid) {
            $db->prepare("UPDATE partners SET name=?,type=?,contact_name=?,contact_telegram=?,affiliate_url=?,notes=? WHERE id=?")
               ->execute([$name,$type,$cname?:null,$ctg?:null,$aurl?:null,$notes?:null,$pid]);
        } else {
            $db->prepare("INSERT INTO partners (name,type,contact_name,contact_telegram,affiliate_url,notes) VALUES (?,?,?,?,?,?)")
               ->execute([$name,$type,$cname?:null,$ctg?:null,$aurl?:null,$notes?:null]);
        }
    }
    header('Location: /admin/partners.php?msg=' . urlencode('Сохранено'));
    exit();
}

if ($action === 'delete') {
    $pid = (int)($_POST['id'] ?? 0);
    if ($pid) {
        $db->prepare("UPDATE brands SET partner_id = NULL WHERE partner_id = ?")->execute([$pid]);
        $db->prepare("DELETE FROM partners WHERE id = ?")->execute([$pid]);
    }
    header('Location: /admin/partners.php?msg=' . urlencode('Партнёрка удалена'));
    exit();
}

if ($action === 'assign') {
    $brand_id  = (int)($_POST['brand_id'] ?? 0);
    $partner_id = (int)($_POST['partner_id'] ?? 0);
    if ($brand_id && $partner_id) {
        $db->prepare("UPDATE brands SET partner_id = ? WHERE id = ?")->execute([$partner_id, $brand_id]);
    }
    header('Location: /admin/partners.php');
    exit();
}

if ($action === 'unassign') {
    $brand_id = (int)($_POST['brand_id'] ?? 0);
    if ($brand_id) {
        $db->prepare("UPDATE brands SET partner_id = NULL WHERE id = ?")->execute([$brand_id]);
    }
    header('Location: /admin/partners.php');
    exit();
}

// Данные
$partners = $db->query("SELECT * FROM partners ORDER BY name ASC")->fetchAll();

$all_brands = $db->query("
    SELECT id, name, partner_id, payment_models, rate_cpa, rate_revshare
    FROM brands WHERE status != 'inactive'
    ORDER BY name ASC
")->fetchAll();

$brands_by_partner = [];
$unassigned_brands = [];
foreach ($all_brands as $b) {
    if ($b['partner_id']) {
        $brands_by_partner[(int)$b['partner_id']][] = $b;
    } else {
        $unassigned_brands[] = $b;
    }
}

// Уникальных GEO на партнёрку (исключаем виртуальные en/crypto)
$geo_raw = $db->query("
    SELECT b.partner_id, COUNT(DISTINCT bc.country_code) as geo_count
    FROM brand_countries bc
    JOIN brands b ON b.id = bc.brand_id
    WHERE b.partner_id IS NOT NULL
      AND b.status != 'inactive'
      AND bc.country_code NOT IN ('en','crypto')
    GROUP BY b.partner_id
")->fetchAll();
$geo_by_partner = array_column($geo_raw, 'geo_count', 'partner_id');

function models_str(array $b): string {
    $models = json_decode($b['payment_models'] ?? '[]', true) ?: [];
    $parts = [];
    if (in_array('cpa', $models) && $b['rate_cpa'] !== null)      $parts[] = 'CPA ' . (int)$b['rate_cpa'];
    if (in_array('revshare', $models) && $b['rate_revshare'] !== null) $parts[] = 'RS ' . $b['rate_revshare'] . '%';
    if (in_array('hybrid', $models))                               $parts[] = 'Hybrid';
    return implode(' · ', $parts);
}
?>
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Партнёрки — Admin</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
    <style>
        .partner-card {
            border: none;
            border-radius: 12px;
            box-shadow: 0 2px 8px rgba(0,0,0,.08);
            transition: transform .15s, box-shadow .15s;
            overflow: hidden;
        }
        .partner-card:hover {
            transform: translateY(-3px);
            box-shadow: 0 8px 24px rgba(0,0,0,.14);
        }
        .partner-card .card-header {
            border-bottom: 1px solid rgba(0,0,0,.06);
            padding: .75rem 1rem;
        }
        .card-accent { height: 5px; width: 100%; }
        .contact-block {
            background: rgba(0,0,0,.03);
            border-radius: 8px;
            padding: .5rem .75rem;
            margin-bottom: .75rem;
        }
        .brand-row {
            background: rgba(0,0,0,.02);
            border-radius: 6px;
            padding: .25rem .5rem;
            margin-bottom: .3rem;
        }
        .brand-row:hover { background: rgba(0,0,0,.05); }
        .pp-icon {
            width: 36px; height: 36px;
            border-radius: 50%;
            display: flex; align-items: center; justify-content: center;
            font-size: 1rem; flex-shrink: 0;
        }
    </style>
</head>
<body>
<?= admin_nav('partners') ?>
<div class="container-fluid px-4">

    <div class="d-flex justify-content-between align-items-center mb-3">
        <h4 class="mb-0">Партнёрки</h4>
        <div class="d-flex gap-2">
            <input type="text" id="ppSearch" class="form-control form-control-sm" placeholder="Поиск по названию..." style="width:220px" oninput="filterCards(this.value)">
            <button class="btn btn-primary btn-sm" onclick="openModal()">+ Добавить ПП</button>
        </div>
    </div>

    <?php if (isset($_GET['msg'])): ?>
        <div class="alert alert-success alert-dismissible fade show py-2">
            <?= h($_GET['msg']) ?>
            <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
        </div>
    <?php endif ?>

    <?php if (empty($partners)): ?>
        <div class="text-center text-muted py-5">
            <p>Партнёрок пока нет.</p>
            <button class="btn btn-primary" onclick="openModal()">+ Добавить первую</button>
        </div>
    <?php else: ?>

    <?php
    $palette = [
        ['#6366f1','#eef2ff','#818cf8'],  // indigo
        ['#ec4899','#fdf2f8','#f472b6'],  // pink
        ['#f59e0b','#fffbeb','#fbbf24'],  // amber
        ['#10b981','#ecfdf5','#34d399'],  // emerald
        ['#3b82f6','#eff6ff','#60a5fa'],  // blue
        ['#ef4444','#fef2f2','#f87171'],  // red
        ['#8b5cf6','#f5f3ff','#a78bfa'],  // violet
        ['#06b6d4','#ecfeff','#22d3ee'],  // cyan
    ];
    ?>
    <div class="row g-3 mb-4">
    <?php foreach ($partners as $idx => $p):
        $p_brands   = $brands_by_partner[(int)$p['id']] ?? [];
        $type_label = $p['type'] === 'network' ? 'Network' : 'Прямой рекл.';
        $type_icon  = $p['type'] === 'network' ? 'bi-diagram-3-fill' : 'bi-person-badge-fill';
        $available  = $unassigned_brands;
        [$accent, $bg_light, $accent_light] = $palette[$idx % count($palette)];
        $geo_count = (int)($geo_by_partner[$p['id']] ?? 0);
    ?>
        <div class="col-md-4 pp-card-col" data-name="<?= h(mb_strtolower($p['name'])) ?>">
            <div class="card partner-card h-100">
                <div class="card-accent" style="background: linear-gradient(90deg, <?= $accent ?>, <?= $accent_light ?>)"></div>
                <div class="card-header d-flex align-items-center gap-2" style="background:<?= $bg_light ?>">
                    <div class="pp-icon" style="background:<?= $accent ?>22; color:<?= $accent ?>">
                        <i class="bi <?= $type_icon ?>"></i>
                    </div>
                    <div class="flex-grow-1 overflow-hidden">
                        <div class="fw-bold text-truncate"><?= h($p['name']) ?></div>
                        <div class="small" style="color:<?= $accent ?>"><?= $type_label ?></div>
                    </div>
                    <div class="d-flex flex-column align-items-end gap-1">
                        <?php if (count($p_brands) === 0): ?>
                        <span class="badge rounded-pill bg-danger">
                            <i class="bi bi-exclamation-triangle-fill me-1"></i>0 брендов
                        </span>
                        <?php else: ?>
                        <span class="badge rounded-pill" style="background:<?= $accent ?>22;color:<?= $accent ?>;border:1px solid <?= $accent ?>44">
                            <i class="bi bi-grid-3x3-gap-fill me-1"></i><?= count($p_brands) ?> брендов
                        </span>
                        <?php endif ?>
                        <span class="badge rounded-pill" style="background:<?= $accent ?>22;color:<?= $accent ?>;border:1px solid <?= $accent ?>44">
                            <i class="bi bi-geo-alt-fill me-1"></i><?= $geo_count ?> GEO
                        </span>
                    </div>
                </div>
                <div class="card-body py-3">

                    <?php if ($p['contact_name'] || $p['contact_telegram'] || $p['affiliate_url']): ?>
                    <div class="contact-block small">
                        <?php if ($p['contact_name']): ?>
                            <div class="mb-1"><i class="bi bi-person me-1" style="color:<?= $accent ?>"></i><strong><?= h($p['contact_name']) ?></strong></div>
                        <?php endif ?>
                        <?php if ($p['contact_telegram']): ?>
                            <div class="mb-1">
                                <i class="bi bi-telegram me-1" style="color:<?= $accent ?>"></i>
                                <a href="https://t.me/<?= h(ltrim($p['contact_telegram'], '@')) ?>" target="_blank" rel="noopener" style="color:<?= $accent ?>">
                                    <?= h($p['contact_telegram']) ?>
                                </a>
                            </div>
                        <?php endif ?>
                        <?php if ($p['affiliate_url']): ?>
                            <div class="text-truncate">
                                <i class="bi bi-link-45deg me-1" style="color:<?= $accent ?>"></i>
                                <a href="<?= h($p['affiliate_url']) ?>" target="_blank" rel="noopener" class="text-muted">
                                    <?= h(preg_replace('#^https?://#', '', $p['affiliate_url'])) ?>
                                </a>
                            </div>
                        <?php endif ?>
                    </div>
                    <?php endif ?>

                    <?php if ($p['notes']): ?>
                    <div class="small text-muted fst-italic mb-2"><?= h($p['notes']) ?></div>
                    <?php endif ?>

                    <div class="small fw-semibold mb-2" style="color:<?= $accent ?>">
                        <i class="bi bi-grid me-1"></i>Бренды
                    </div>

                    <?php foreach ($p_brands as $b): ?>
                        <?php $ms = models_str($b); ?>
                        <div class="brand-row d-flex justify-content-between align-items-center">
                            <span class="text-truncate me-1">
                                <a href="/admin/brand_edit.php?id=<?= $b['id'] ?>" class="text-decoration-none small fw-medium">
                                    <?= h($b['name']) ?>
                                </a>
                                <?php if ($ms): ?>
                                    <span class="badge ms-1" style="background:<?= $accent ?>22;color:<?= $accent ?>;font-size:.68rem"><?= h($ms) ?></span>
                                <?php endif ?>
                            </span>
                            <form method="post" class="flex-shrink-0">
                                <input type="hidden" name="action" value="unassign">
                                <input type="hidden" name="brand_id" value="<?= $b['id'] ?>">
                                <button class="btn btn-link btn-sm p-0 text-danger lh-1" title="Отвязать"
                                        onclick="return confirm('Отвязать «<?= h(addslashes($b['name'])) ?>» от ПП?')">
                                    <i class="bi bi-x-lg" style="font-size:.7rem"></i>
                                </button>
                            </form>
                        </div>
                    <?php endforeach ?>

                    <?php if (!empty($available)): ?>
                    <form method="post" class="mt-2 d-flex gap-1">
                        <input type="hidden" name="action" value="assign">
                        <input type="hidden" name="partner_id" value="<?= $p['id'] ?>">
                        <select name="brand_id" class="form-select form-select-sm">
                            <option value="">+ Привязать бренд...</option>
                            <?php foreach ($available as $ab): ?>
                                <option value="<?= $ab['id'] ?>"><?= h($ab['name']) ?></option>
                            <?php endforeach ?>
                        </select>
                        <button class="btn btn-sm flex-shrink-0" style="background:<?= $accent ?>;color:#fff;border:none"
                                onclick="return this.form.brand_id.value !== ''">Ок</button>
                    </form>
                    <?php endif ?>

                </div>
                <div class="card-footer py-2 d-flex gap-2" style="background:<?= $bg_light ?>;border-top:1px solid <?= $accent ?>22">
                    <button class="btn btn-sm flex-fill fw-medium"
                            style="background:<?= $accent ?>15;color:<?= $accent ?>;border:1px solid <?= $accent ?>33"
                            onclick="openModal(<?= htmlspecialchars(json_encode($p), ENT_QUOTES) ?>)">
                        <i class="bi bi-pencil"></i> Ред.
                    </button>
                    <form method="post" class="flex-fill" onsubmit="return confirm('Удалить «<?= h(addslashes($p['name'])) ?>»?')">
                        <input type="hidden" name="action" value="delete">
                        <input type="hidden" name="id" value="<?= $p['id'] ?>">
                        <button class="btn btn-sm btn-outline-danger w-100"><i class="bi bi-trash"></i> Удалить</button>
                    </form>
                </div>
            </div>
        </div>
    <?php endforeach ?>
    </div>

    <div id="ppNoResults" class="text-center text-muted py-4" style="display:none">Ничего не найдено</div>

    <?php endif ?>

    <?php if (!empty($unassigned_brands)): ?>
    <div class="card mb-4 border-warning">
        <div class="card-header bg-warning bg-opacity-10 fw-semibold">
            Бренды без ПП (<?= count($unassigned_brands) ?>)
        </div>
        <div class="card-body p-2">
            <div class="d-flex flex-wrap gap-2">
            <?php foreach ($unassigned_brands as $b): ?>
                <a href="/admin/brand_edit.php?id=<?= $b['id'] ?>" class="badge bg-secondary text-decoration-none">
                    <?= h($b['name']) ?>
                </a>
            <?php endforeach ?>
            </div>
        </div>
    </div>
    <?php endif ?>

</div>

<!-- Modal: создание / редактирование ПП -->
<div class="modal fade" id="partnerModal" tabindex="-1">
    <div class="modal-dialog">
        <form method="post" class="modal-content">
            <input type="hidden" name="action" value="save">
            <input type="hidden" name="id" id="modal_id" value="0">
            <div class="modal-header py-2">
                <h6 class="modal-title mb-0" id="modalTitle">Новая партнёрка</h6>
                <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
            </div>
            <div class="modal-body">
                <div class="row g-3">
                    <div class="col-8">
                        <label class="form-label">Название ПП *</label>
                        <input type="text" name="name" id="modal_name" class="form-control" required placeholder="Имя партнёрки">
                    </div>
                    <div class="col-4">
                        <label class="form-label">Тип</label>
                        <select name="type" id="modal_type" class="form-select">
                            <option value="direct">Прямой рекл.</option>
                            <option value="network">Network</option>
                        </select>
                    </div>
                    <div class="col-6">
                        <label class="form-label">Имя контакта</label>
                        <input type="text" name="contact_name" id="modal_cname" class="form-control" placeholder="Иван Иванов">
                    </div>
                    <div class="col-6">
                        <label class="form-label">Telegram</label>
                        <input type="text" name="contact_telegram" id="modal_ctg" class="form-control" placeholder="@username">
                    </div>
                    <div class="col-12">
                        <label class="form-label">Адрес платформы</label>
                        <input type="url" name="affiliate_url" id="modal_aurl" class="form-control" placeholder="https://...">
                    </div>
                    <div class="col-12">
                        <label class="form-label">Заметки</label>
                        <textarea name="notes" id="modal_notes" class="form-control" rows="2" placeholder="Доп. информация"></textarea>
                    </div>
                </div>
            </div>
            <div class="modal-footer py-2">
                <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Отмена</button>
                <button type="submit" class="btn btn-primary btn-sm">Сохранить</button>
            </div>
        </form>
    </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
var partnerModal = new bootstrap.Modal(document.getElementById('partnerModal'));

function filterCards(q) {
    var q2 = q.trim().toLowerCase();
    var empty = true;
    document.querySelectorAll('.pp-card-col').forEach(function(col) {
        var match = !q2 || col.dataset.name.indexOf(q2) !== -1;
        col.style.display = match ? '' : 'none';
        if (match) empty = false;
    });
    var hint = document.getElementById('ppNoResults');
    if (hint) hint.style.display = empty ? '' : 'none';
}

function openModal(p) {
    document.getElementById('modal_id').value    = p ? p.id : 0;
    document.getElementById('modal_name').value  = p ? p.name : '';
    document.getElementById('modal_type').value  = p ? p.type : 'direct';
    document.getElementById('modal_cname').value = p ? (p.contact_name || '') : '';
    document.getElementById('modal_ctg').value   = p ? (p.contact_telegram || '') : '';
    document.getElementById('modal_aurl').value  = p ? (p.affiliate_url || '') : '';
    document.getElementById('modal_notes').value = p ? (p.notes || '') : '';
    document.getElementById('modalTitle').textContent = p ? 'Редактировать ПП' : 'Новая партнёрка';
    partnerModal.show();
}
</script>
</body>
</html>